diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php index 73e03a4..db62ab2 100644 --- a/core/modules/views/src/Plugin/views/field/Field.php +++ b/core/modules/views/src/Plugin/views/field/Field.php @@ -977,11 +977,15 @@ protected function getTableMapping() { * {@inheritdoc} */ public function getValue(ResultRow $values, $field = NULL) { + $entity = $this->getEntity($values); + $langcode = $this->getFieldLangcode($entity, $values); + $entity = $entity->getTranslation($langcode); + if ($field === NULL) { - return $this->getEntity($values)->{$this->definition['field_name']}->value; + return $entity->{$this->definition['field_name']}->value; } - return $this->getEntity($values)->{$this->definition['field_name']}->$field; + return $entity->{$this->definition['field_name']}->$field; } } diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php index fdcc37f..b525120 100644 --- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php +++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php @@ -259,7 +259,8 @@ public function buildJoin($select_query, $table, $view_query) { $left_field = "$left_table[alias].$this->leftField"; } else { - // This can be used if left_field is a formula or something. It should be used only *very* rarely. + // This can be used if left_field is a formula or something. It should be + // used only *very* rarely. $left_field = $this->leftField; $left_table = NULL; } diff --git a/core/modules/views/src/Tests/FieldApiDataTest.php b/core/modules/views/src/Tests/FieldApiDataTest.php index 96ff642..b538aed 100644 --- a/core/modules/views/src/Tests/FieldApiDataTest.php +++ b/core/modules/views/src/Tests/FieldApiDataTest.php @@ -7,7 +7,12 @@ namespace Drupal\views\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\field\Tests\Views\FieldTestBase; +use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\node\Entity\Node; +use Drupal\node\Entity\NodeType; +use Drupal\views\Views; /** * Tests the Field Views data. @@ -16,10 +21,27 @@ */ class FieldApiDataTest extends FieldTestBase { + /** + * {@inheritdoc} + */ + public static $modules = ['language']; + + /** + * {@inheritdoc} + */ + public static $testViews = ['test_field_config_translation_filter']; + + /** + * The nodes used by the translation filter tests. + * + * @var \Drupal\node\NodeInterface[] + */ + protected $translationNodes; + protected function setUp() { - parent::setUp(); + parent::setUp(FALSE); - $field_names = $this->setUpFieldStorages(1); + $field_names = $this->setUpFieldStorages(4); // Attach the field to nodes only. $field = array( @@ -36,6 +58,96 @@ protected function setUp() { ); $nodes[] = $this->drupalCreateNode($edit); } + + $bundles = []; + $bundles[] = $bundle = NodeType::create(['type' => 'bundle1']); + $bundle->save(); + $bundles[] = $bundle = NodeType::create(['type' => 'bundle2']); + $bundle->save(); + + // Make the first field translatable on all bundles. + $field = FieldConfig::create([ + 'field_name' => $field_names[1], + 'entity_type' => 'node', + 'bundle' => $bundles[0]->id(), + 'translatable' => TRUE, + ]); + $field->save(); + $field = FieldConfig::create([ + 'field_name' => $field_names[1], + 'entity_type' => 'node', + 'bundle' => $bundles[1]->id(), + 'translatable' => TRUE, + ]); + $field->save(); + + // Make the second field not translatable on any bundle. + $field = FieldConfig::create([ + 'field_name' => $field_names[2], + 'entity_type' => 'node', + 'bundle' => $bundles[0]->id(), + 'translatable' => FALSE, + ]); + $field->save(); + $field = FieldConfig::create([ + 'field_name' => $field_names[2], + 'entity_type' => 'node', + 'bundle' => $bundles[1]->id(), + 'translatable' => FALSE, + ]); + $field->save(); + + // Make the last field translatable on some bundles. + $field = FieldConfig::create([ + 'field_name' => $field_names[3], + 'entity_type' => 'node', + 'bundle' => $bundles[0]->id(), + 'translatable' => TRUE, + ]); + $field->save(); + $field = FieldConfig::create([ + 'field_name' => $field_names[3], + 'entity_type' => 'node', + 'bundle' => $bundles[1]->id(), + 'translatable' => FALSE, + ]); + $field->save(); + + // Create some example content + ConfigurableLanguage::create([ + 'id' => 'es', + ])->save(); + ConfigurableLanguage::create([ + 'id' => 'fr', + ])->save(); + + $node = Node::create([ + 'type' => $bundles[0]->id(), + 'langcode' => 'es', + $field_names[1] => 'field name 1: es', + $field_names[2] => 'field name 2: es', + $field_names[3] => 'field name 3: es', + ]); + $node->save(); + $this->translationNodes[] = $node; + $translation = $node->getTranslation('fr'); + $translation->{$field_names[1]}->value = 'field name 1: fr'; + $translation->{$field_names[3]}->value = 'field name 3: fr'; + $translation->save(); + + $node = Node::create([ + 'type' => $bundles[1]->id(), + 'langcode' => 'es', + $field_names[1] => 'field name 1: es', + $field_names[2] => 'field name 2: es', + $field_names[3] => 'field name 3: es', + ]); + $node->save(); + $this->translationNodes[] = $node; + $translation = $node->getTranslation('fr'); + $translation->{$field_names[1]}->value = 'field name 1: fr'; + $translation->save(); + } /** @@ -43,7 +155,7 @@ protected function setUp() { * * We check data structure for both node and node revision tables. */ - function testViewsData() { + function ptestViewsData() { $views_data = $this->container->get('views.views_data'); $data = array(); @@ -88,4 +200,119 @@ function testViewsData() { $this->assertTrue(empty($data[$revision_table][$field_storage->getName()]['field']['click sortable']), 'Non-primary fields are not click sortable'); } + /** + * Tests filtering entries with different translatabilty. + */ + public function testEntityFieldFilter() { + $map = [ + 'nid' => 'nid', + 'langcode' => 'langcode', + ]; + + $view = Views::getView('test_field_config_translation_filter'); + + // Filter by 'field name 1: es'. + $view->setDisplay('embed_1'); + $this->executeView($view); + $expected = [ + [ + 'nid' => $this->translationNodes[0]->id(), + 'langcode' => 'es', + ], + [ + 'nid' => $this->translationNodes[1]->id(), + 'langcode' => 'es', + ], + ]; + + $this->assertIdenticalResultset($view, $expected, $map); + $view->destroy(); + + // Filter by 'field name 1: fr'. + $view->setDisplay('embed_2'); + $this->executeView($view); + $expected = [ + [ + 'nid' => $this->translationNodes[0]->id(), + 'langcode' => 'fr', + ], + [ + 'nid' => $this->translationNodes[1]->id(), + 'langcode' => 'fr', + ], + ]; + + $this->assertIdenticalResultset($view, $expected, $map); + $view->destroy(); + + // Filter by 'field name 2: es'. + $view->setDisplay('embed_3'); + $this->executeView($view); + $expected = [ + [ + 'nid' => $this->translationNodes[0]->id(), + 'langcode' => 'es', + ], + [ + 'nid' => $this->translationNodes[0]->id(), + 'langcode' => 'fr', + ], + [ + 'nid' => $this->translationNodes[1]->id(), + 'langcode' => 'es', + ], + [ + 'nid' => $this->translationNodes[1]->id(), + 'langcode' => 'fr', + ], + ]; + + $this->assertIdenticalResultset($view, $expected, $map); + $view->destroy(); + + // Filter by 'field name 2: fr', which doesn't exist. + $view->setDisplay('embed_4'); + $this->executeView($view); + $expected = [ + ]; + + $this->assertIdenticalResultset($view, $expected, $map); + $view->destroy(); + + // Filter by 'field name 3: es'. + $view->setDisplay('embed_5'); + $this->executeView($view); + $expected = [ + [ + 'nid' => $this->translationNodes[0]->id(), + 'langcode' => 'es', + ], + [ + 'nid' => $this->translationNodes[1]->id(), + 'langcode' => 'es', + ], + // Why is this one returned? + [ + 'nid' => $this->translationNodes[1]->id(), + 'langcode' => 'fr', + ], + ]; + + $this->assertIdenticalResultset($view, $expected, $map); + $view->destroy(); + + // Filter by 'field name 3: fr'. + $view->setDisplay('embed_6'); + $this->executeView($view); + $expected = [ + [ + 'nid' => $this->translationNodes[0]->id(), + 'langcode' => 'fr', + ], + ]; + + $this->assertIdenticalResultset($view, $expected, $map); + $view->destroy(); + } + } diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_config_translation_filter.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_config_translation_filter.yml new file mode 100644 index 0000000..146f280 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_config_translation_filter.yml @@ -0,0 +1,173 @@ +langcode: en +status: true +dependencies: { } +id: test_field_config_translation_filter +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: id +core: '8' +display: + default: + display_options: + access: + type: none + cache: + type: none + fields: + nid: + id: nid + field: nid + table: node_field_data + plugin_id: field + entity_type: node + entity_type: nid + langcode: + id: langcode + field: langcode + table: node_field_data + plugin_id: field + entity_type: node + entity_field: langcode + field_name_1: + id: field_name_1 + table: node__field_name_1 + field: field_name_1 + plugin_id: field + entity_type: node + entity_field: field_name_1 + field_name_2: + id: field_name_2 + table: node__field_name_2 + field: field_name_2 + plugin_id: field + entity_type: node + entity_field: field_name_2 + field_name_3: + id: field_name_3 + table: node__field_name_3 + field: field_name_3 + plugin_id: field + entity_type: node + entity_field: field_name_3 + sorts: + nid: + id: nid + table: node_field_data + field: nid + order: ASC + plugin_id: standard + entity_type: node + entity_field: nid + style: + type: html_list + row: + type: fields + display_plugin: default + display_title: Master + id: default + position: 0 + embed_1: + display_options: + defaults: + fields: true + filters: false + filters: + field_name_1_value: + id: field_name_1_value + table: node__field_name_1 + field: field_name_1_value + value: 'field name 1: es' + plugin_id: string + entity_type: node + entity_field: field_name_1 + display_plugin: embed + display_title: Embed 1 + id: embed_1 + position: 1 + embed_2: + display_options: + defaults: + filters: false + filters: + field_name_1_value: + id: field_name_1_value + table: node__field_name_1 + field: field_name_1_value + value: 'field name 1: fr' + plugin_id: string + entity_type: node + entity_field: field_name_1 + display_plugin: embed + display_title: Embed 2 + id: embed_2 + position: 2 + embed_3: + display_options: + defaults: + filters: false + filters: + field_name_2_value: + id: field_name_2_value + table: node__field_name_2 + field: field_name_2_value + value: 'field name 2: es' + plugin_id: string + entity_type: node + entity_field: field_name_2 + display_plugin: embed + display_title: Embed 3 + id: embed_3 + position: 3 + embed_4: + display_options: + defaults: + filters: false + filters: + field_name_2_value: + id: field_name_2_value + table: node__field_name_2 + field: field_name_2_value + value: 'field name 2: fr' + plugin_id: string + entity_type: node + entity_field: field_name_2 + display_plugin: embed + display_title: Embed 4 + id: embed_4 + position: 4 + embed_5: + display_options: + defaults: + filters: false + filters: + field_name_3_value: + id: field_name_3_value + table: node__field_name_3 + field: field_name_3_value + value: 'field name 3: es' + plugin_id: string + entity_type: node + entity_field: field_name_3 + display_plugin: embed + display_title: Embed 5 + id: embed_5 + position: 5 + embed_6: + display_options: + defaults: + filters: false + filters: + field_name_3_value: + id: field_name_3_value + table: node__field_name_3 + field: field_name_3_value + value: 'field name 3: fr' + plugin_id: string + entity_type: node + entity_field: field_name_3 + display_plugin: embed + display_title: Embed 6 + id: embed_6 + position: 6 diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc index ed872d1..ad5e281 100644 --- a/core/modules/views/views.views.inc +++ b/core/modules/views/views.views.inc @@ -359,11 +359,13 @@ function views_field_default_views_data(FieldStorageConfigInterface $field_stora } } - // If the field is translatable on all the bundles, there will be a join on the langcode. + // If the field is translatable on all the bundles, there will be a join on + // the langcode. if (!empty($translatable_configs) && empty($untranslatable_configs)) { $translation_join_type = 'language'; } - // If the field is translatable only on certain bundles, there will be a join on langcode OR bundle name. + // If the field is translatable only on certain bundles, there will be a join + // on langcode OR bundle name. elseif (!empty($translatable_configs) && !empty($untranslatable_configs)) { foreach ($untranslatable_configs as $config) { $untranslatable_config_bundles[] = $config->getTargetBundle();