Commit 62bb5c4d authored by alexpott's avatar alexpott

Issue #2218065 by jhodgdon, dawehner: Fixed Need to join fields to the entity...

Issue #2218065 by jhodgdon, dawehner: Fixed Need to join fields to the entity field data tables, not entity tables, or filtering increases number of results.
parent 0463b81a
<?php
/**
* @file
* Contains \Drupal\comment\Tests\Views\CommentFieldFilterTest.
*/
namespace Drupal\comment\Tests\Views;
use Drupal\Core\Language\Language;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests comment field filters with translations.
*
* @group comment
*/
class CommentFieldFilterTest extends CommentTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('language');
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_field_filters');
/**
* List of comment titles by language.
*
* @var array
*/
public $comment_titles = array();
function setUp() {
parent::setUp();
// Add two new languages.
$language = new Language(array(
'id' => 'fr',
'name' => 'French',
));
language_save($language);
$language = new Language(array(
'id' => 'es',
'name' => 'Spanish',
));
language_save($language);
// Make the comment body field translatable. The title is already
// translatable by definition.
$field = FieldStorageConfig::loadByName('comment', 'comment_body');
$field->translatable = TRUE;
$field->save();
// Set up comment titles.
$this->comment_titles = array(
'en' => 'Food in Paris',
'es' => 'Comida en Paris',
'fr' => 'Nouriture en Paris',
);
// Create a new comment. Using the one created earlier will not work,
// as it predates the language set-up.
$comment = array(
'uid' => $this->loggedInUser->id(),
'entity_id' => $this->node_user_commented->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'cid' => '',
'pid' => '',
'node_type' => '',
);
$this->comment = entity_create('comment', $comment);
// Add field values and translate the comment.
$this->comment->subject->value = $this->comment_titles['en'];
$this->comment->comment_body->value = $this->comment_titles['en'];
$this->comment->langcode = 'en';
$this->comment->save();
foreach (array('es', 'fr') as $langcode) {
$translation = $this->comment->addTranslation($langcode, array());
$translation->comment_body->value = $this->comment_titles[$langcode];
$translation->subject->value = $this->comment_titles[$langcode];
}
$this->comment->save();
}
/**
* Tests body and title filters.
*/
public function testFilters() {
// Test the title filter page, which filters for title contains 'Comida'.
// Should show just the Spanish translation, once.
$this->assertPageCounts('test-title-filter', array('es' => 1, 'fr' => 0, 'en' => 0), 'Comida title filter');
// Test the body filter page, which filters for body contains 'Comida'.
// Should show just the Spanish translation, once.
$this->assertPageCounts('test-body-filter', array('es' => 1, 'fr' => 0, 'en' => 0), 'Comida body filter');
// Test the title Paris filter page, which filters for title contains
// 'Paris'. Should show each translation once.
$this->assertPageCounts('test-title-paris', array('es' => 1, 'fr' => 1, 'en' => 1), 'Paris title filter');
// Test the body Paris filter page, which filters for body contains
// 'Paris'. Should show each translation once.
$this->assertPageCounts('test-body-paris', array('es' => 1, 'fr' => 1, 'en' => 1), 'Paris body filter');
}
/**
* Asserts that the given comment translation counts are correct.
*
* @param string $path
* Path of the page to test.
* @param array $counts
* Array whose keys are languages, and values are the number of times
* that translation should be shown on the given page.
* @param string $message
* Message suffix to display.
*/
protected function assertPageCounts($path, $counts, $message) {
// Get the text of the page.
$this->drupalGet($path);
$text = $this->getTextContent();
// Check the counts. Note that the title and body are both shown on the
// page, and they are the same. So the title/body string should appear on
// the page twice as many times as the input count.
foreach ($counts as $langcode => $count) {
$this->assertEqual(substr_count($text, $this->comment_titles[$langcode]), 2 * $count, 'Translation ' . $langcode . ' has count ' . $count . ' with ' . $message);
}
}
}
uuid: b9f5216c-231d-477b-b312-18020246cbb0
langcode: en
status: true
dependencies:
module:
- comment
id: test_field_filters
label: 'Test field filters'
module: views
description: ''
tag: ''
base_table: comment
base_field: cid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
provider: views
display_options:
access:
type: perm
options:
perm: 'access content'
provider: user
dependencies: { }
cache:
type: none
options: { }
provider: views
dependencies: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: false
query_tags: { }
provider: views
dependencies: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
provider: views
dependencies: { }
pager:
type: none
options:
items_per_page: 0
offset: 0
style:
type: default
row:
type: 'entity:comment'
options:
links: true
view_mode: full
rendering_language: translation_language_renderer
relationships:
node:
id: node
table: comment_field_data
field: node
required: true
plugin_id: standard
provider: views
relationship: none
group_type: group
admin_label: Content
dependencies: { }
fields:
subject:
id: subject
table: comment_field_data
field: subject
provider: comment
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
link_to_comment: 1
relationship: none
group_type: group
admin_label: ''
dependencies: { }
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
link_to_entity: false
filters:
subject:
id: subject
table: comment_field_data
field: subject
relationship: none
group_type: group
admin_label: ''
dependencies:
module:
- views
operator: contains
value: Comida
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
provider: views
sorts: { }
title: 'Title filter page'
header: { }
footer: { }
empty: { }
arguments: { }
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode_add_to_query: null
page_tc:
display_plugin: page
id: page_tc
display_title: 'Title Comida'
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode_add_to_query: null
path: test-title-filter
display_description: ''
page_bp:
display_plugin: page
id: page_bp
display_title: 'Body Paris'
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode_add_to_query: null
path: test-body-paris
display_description: ''
filters:
comment_body_value:
id: comment_body_value
table: comment__comment_body
field: comment_body_value
relationship: none
group_type: group
admin_label: ''
dependencies:
module:
- views
operator: contains
value: Paris
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
provider: views
defaults:
filters: false
filter_groups: false
title: false
filter_groups:
operator: AND
groups:
1: AND
title: 'Body filter page'
page_tp:
display_plugin: page
id: page_tp
display_title: 'Title Paris'
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode_add_to_query: null
path: test-title-paris
display_description: ''
filters:
subject:
id: subject
table: comment_field_data
field: subject
relationship: none
group_type: group
admin_label: ''
dependencies:
module:
- views
- views
operator: contains
value: Paris
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
provider: views
defaults:
filters: false
filter_groups: false
filter_groups:
operator: AND
groups:
1: AND
page_bf:
display_plugin: page
id: page_bf
display_title: 'Body Comida'
position: 1
provider: views
display_options:
field_langcode: '***CURRENT_LANGUAGE***'
field_langcode_add_to_query: null
path: test-body-filter
display_description: ''
filters:
comment_body_value:
id: comment_body_value
table: comment__comment_body
field: comment_body_value
relationship: none
group_type: group
admin_label: ''
dependencies:
module:
- views
- views
operator: contains
value: Comida
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
plugin_id: string
provider: views
defaults:
filters: false
filter_groups: false
title: false
filter_groups:
operator: AND
groups:
1: AND
title: 'Body filter page'
......@@ -125,17 +125,28 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
$field_columns = $field_storage->getColumns();
// Grab information about the entity type tables.
// We need to join to both the base table and the data table, if available.
$entity_manager = \Drupal::entityManager();
$entity_type_id = $field_storage->entity_type;
$entity_type = $entity_manager->getDefinition($entity_type_id);
if (!$entity_table = $entity_type->getBaseTable()) {
if (!$base_table = $entity_type->getBaseTable()) {
// We cannot do anything if for some reason there is no base table.
return $data;
}
$entity_tables = array($entity_table => $entity_type_id);
$entity_tables = array($base_table => $entity_type_id);
// Some entities may not have a data table.
$data_table = $entity_type->getDataTable();
if ($data_table) {
$entity_tables[$data_table] = $entity_type_id;
}
$entity_revision_table = $entity_type->getRevisionTable();
$supports_revisions = $entity_type->hasKey('revision') && $entity_revision_table;
if ($supports_revisions) {
$entity_tables[$entity_revision_table] = $entity_type_id;
$entity_revision_data_table = $entity_type->getRevisionDataTable();
if ($entity_revision_data_table) {
$entity_tables[$entity_revision_data_table] = $entity_type_id;
}
}
// Description of the field tables.
......@@ -154,24 +165,55 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
// Build the relationships between the field table and the entity tables.
$table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_CURRENT]['alias'];
$data[$table_alias]['table']['join'][$entity_table] = array(
'left_field' => $entity_type->getKey('id'),
'field' => 'entity_id',
'extra' => array(
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
),
);
if ($supports_revisions) {
$table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION]['alias'];
$data[$table_alias]['table']['join'][$entity_revision_table] = array(
'left_field' => $entity_type->getKey('revision'),
'field' => 'revision_id',
if ($data_table) {
// Tell Views how to join to the base table, via the data table.
$data[$table_alias]['table']['join'][$base_table] = array(
'left_table' => $data_table,
'left_field' => $entity_type->getKey('id'),
'field' => 'entity_id',
'extra' => array(
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
array('left_field' => 'langcode', 'field' => 'langcode'),
),
);
}
else {
// If there is no data table, just join directly.
$data[$table_alias]['table']['join'][$base_table] = array(
'left_field' => $entity_type->getKey('id'),
'field' => 'entity_id',
'extra' => array(
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
),
);
}
if ($supports_revisions) {
$table_alias = $field_tables[EntityStorageInterface::FIELD_LOAD_REVISION]['alias'];
if ($entity_revision_data_table) {
// Tell Views how to join to the revision table, via the data table.
$data[$table_alias]['table']['join'][$entity_revision_table] = array(
'left_table' => $entity_revision_data_table,
'left_field' => $entity_type->getKey('revision'),
'field' => 'revision_id',
'extra' => array(
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
array('left_field' => 'langcode', 'field' => 'langcode'),
),
);
}
else {
// If there is no data table, just join directly.
$data[$table_alias]['table']['join'][$entity_revision_table] = array(
'left_field' => $entity_type->getKey('revision'),
'field' => 'revision_id',
'extra' => array(
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
),
);
}
}
$group_name = $entity_type->getLabel();
// Get the list of bundles the field appears in.
$bundles_names = $field_storage->getBundles();
......@@ -214,7 +256,7 @@ function field_views_field_default_views_data(FieldStorageConfigInterface $field
if ($type == EntityStorageInterface::FIELD_LOAD_CURRENT) {
if ($label != $label_name) {
$aliases[] = array(
'base' => $entity_table,
'base' => $base_table,
'group' => $group_name,
'title' => $label_name,
'help' => t('This is an alias of @group: @field.', array('@group' => $group_name, '@field' => $label)),
......
......@@ -63,18 +63,22 @@ function testViewsData() {
$this->assertTrue(isset($data[$revision_table]['table']['join']['node_revision']));
$expected_join = array(
'left_table' => 'node_field_data',
'left_field' => 'nid',
'field' => 'entity_id',
'extra' => array(
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
array('left_field' => 'langcode', 'field' => 'langcode'),
),
);
$this->assertEqual($expected_join, $data[$current_table]['table']['join']['node']);
$expected_join = array(
'left_table' => 'node_field_revision',
'left_field' => 'vid',
'field' => 'revision_id',
'extra' => array(
array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
array('left_field' => 'langcode', 'field' => 'langcode'),
),
);
$this->assertEqual($expected_join, $data[$revision_table]['table']['join']['node_revision']);
......
<?php
/**
* @file
* Contains \Drupal\node\Tests\Views\NodeFieldFilterTest.
*/
namespace Drupal\node\Tests\Views;
use Drupal\Core\Language\Language;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests node field filters with translations.
*
* @group node
*/
class NodeFieldFilterTest extends NodeTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('language');
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_field_filters');
/**
* List of node titles by language.
*
* @var array
*/
public $node_titles = array();
function setUp() {
parent::setUp();
// Create Page content type.
if ($this->profile != 'standard') {
$this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
}
// Add two new languages.
$language = new Language(array(
'id' => 'fr',
'name' => 'French',
));
language_save($language);
$language = new Language(array(
'id' => 'es',
'name' => 'Spanish',
));
language_save($language);
// Make the body field translatable. The title is already translatable by
// definition.