diff --git a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php index 11dbb4ab87..a292efe271 100644 --- a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php +++ b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php @@ -16,4 +16,12 @@ public function getLangcode(ResultRow $row) { return $row->_entity->getUntranslated()->language()->getId(); } + /** + * {@inheritdoc} + */ + public function getLangcodeByRelationship(ResultRow $row, $relationship = 'none') { + $entity = $this->getEntity($row, $relationship); + return $entity->getUntranslated()->language()->getId(); + } + } diff --git a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php index a77f18ae41..c285d0b96f 100644 --- a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php +++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php @@ -238,7 +238,8 @@ protected function buildFields(array $values) { $field = $this->view->field[current($field_ids)]; foreach ($values as $result_row) { if ($entity = $field->getEntity($result_row)) { - $entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslation($entity, $result_row); + $relationship = isset($field->options['relationship']) ? $field->options['relationship'] : 'none'; + $entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslationByRelationship($entity, $result_row, $relationship); } } diff --git a/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php b/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php index dd7aa09471..8d897d57d8 100644 --- a/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php +++ b/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php @@ -67,13 +67,30 @@ protected function getEntityTranslationRenderer() { * The entity translation object for the specified row. */ public function getEntityTranslation(EntityInterface $entity, ResultRow $row) { + return $this->getEntityTranslationByRelationship($entity, $row); + } + + /** + * Returns the entity translation matching the configured row language. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity object the field value being processed is attached to. + * @param \Drupal\views\ResultRow $row + * The result row the field value being processed belongs to. + * @param string $relationship + * The relationship to be used, or 'none' by default. + * + * @return \Drupal\Core\Entity\FieldableEntityInterface + * The entity translation object for the specified row. + */ + public function getEntityTranslationByRelationship(EntityInterface $entity, ResultRow $row, $relationship = 'none') { // We assume the same language should be used for all entity fields // belonging to a single row, even if they are attached to different entity // types. Below we apply language fallback to ensure a valid value is always // picked. $translation = $entity; if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) { - $langcode = $this->getEntityTranslationRenderer()->getLangcode($row); + $langcode = $this->getEntityTranslationRenderer()->getLangcodeByRelationship($row, $relationship); $translation = $this->getEntityRepository()->getTranslationFromContext($entity, $langcode); } return $translation; diff --git a/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php b/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php index 91cf232538..4915b757c8 100644 --- a/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php +++ b/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php @@ -21,6 +21,22 @@ abstract class EntityTranslationRendererBase extends RendererBase { */ abstract public function getLangcode(ResultRow $row); + /** + * Returns the language code associated with the given row. + * + * @param \Drupal\views\ResultRow $row + * The result row. + * @param string $relationship + * The relationship to be used. + * + * @return string + */ + public function getLangcodeByRelationship(ResultRow $row, $relationship) { + // This method needs to be overridden if the relationship is needed in the + // implementation of getLangcode(). + return $this->getLangcode($row); + } + /** * {@inheritdoc} */ @@ -31,15 +47,25 @@ public function query(QueryPluginBase $query, $relationship = NULL) { * {@inheritdoc} */ public function preRender(array $result) { + $this->preRenderByRelationship($result, 'none'); + } + + /** + * Runs before each entity is rendered if a relationship is needed. + * + * @param \Drupal\views\ResultRow[] $result + * The full array of results from the query. + * @param string $relationship + * The relationship to be used. + */ + public function preRenderByRelationship(array $result, $relationship) { $view_builder = \Drupal::entityTypeManager()->getViewBuilder($this->entityType->id()); - /** @var \Drupal\views\ResultRow $row */ foreach ($result as $row) { - // @todo Take relationships into account. - // See https://www.drupal.org/node/2457999. - $entity = $row->_entity; - $entity->view = $this->view; - $this->build[$entity->id()] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row)); + if ($entity = $this->getEntity($row, $relationship)) { + $entity->view = $this->view; + $this->build[$entity->id()] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcodeByRelationship($row, $relationship)); + } } } @@ -47,8 +73,48 @@ public function preRender(array $result) { * {@inheritdoc} */ public function render(ResultRow $row) { - $entity_id = $row->_entity->id(); - return $this->build[$entity_id]; + return $this->renderByRelationship($row, 'none'); + } + + /** + * Renders entity data. + * + * @param \Drupal\views\ResultRow $row + * A single row of the query result. + * @param string $relationship + * The relationship to be used. + * + * @return array + * A renderable array for the entity data contained in the result row. + */ + public function renderByRelationship(ResultRow $row, $relationship) { + if ($entity = $this->getEntity($row, $relationship)) { + $entity_id = $entity->id(); + return $this->build[$entity_id]; + } + return []; + } + + /** + * Gets the entity associated with a row. + * + * @param \Drupal\views\ResultRow $row + * The result row. + * @param string $relationship + * (optional) The relationship. + * + * @return \Drupal\Core\Entity\EntityInterface|null + * The entity might be optional, because the relationship entity might not + * always exist. + */ + protected function getEntity($row, $relationship = 'none') { + if ($relationship === 'none') { + return $row->_entity; + } + elseif (isset($row->_relationship_entities[$relationship])) { + return $row->_relationship_entities[$relationship]; + } + return NULL; } } diff --git a/core/modules/views/src/Entity/Render/RendererBase.php b/core/modules/views/src/Entity/Render/RendererBase.php index 60327f615f..b47dd43081 100644 --- a/core/modules/views/src/Entity/Render/RendererBase.php +++ b/core/modules/views/src/Entity/Render/RendererBase.php @@ -93,7 +93,7 @@ public function getCacheTags() { /** * Runs before each entity is rendered. * - * @param $result + * @param \Drupal\views\ResultRow[] $result * The full array of results from the query. */ public function preRender(array $result) { diff --git a/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php b/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php index f769f73395..1c7e92ce02 100644 --- a/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php +++ b/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php @@ -79,25 +79,27 @@ protected function getLangcodeTable(QueryPluginBase $query, $relationship) { /** * {@inheritdoc} */ - public function preRender(array $result) { + public function preRenderByRelationship(array $result, $relationship) { $view_builder = \Drupal::entityTypeManager()->getViewBuilder($this->entityType->id()); /** @var \Drupal\views\ResultRow $row */ foreach ($result as $row) { - $entity = $row->_entity; - $entity->view = $this->view; - $langcode = $this->getLangcode($row); - $this->build[$entity->id()][$langcode] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $this->getLangcode($row)); + if ($entity = $this->getEntity($row, $relationship)) { + $entity->view = $this->view; + $langcode = $this->getLangcodeByRelationship($row, $relationship); + $this->build[$entity->id()][$langcode] = $view_builder->view($entity, $this->view->rowPlugin->options['view_mode'], $langcode); + } } } /** * {@inheritdoc} */ - public function render(ResultRow $row) { - $entity_id = $row->_entity->id(); - $langcode = $this->getLangcode($row); - return $this->build[$entity_id][$langcode]; + public function renderByRelationship(ResultRow $row, $relationship) { + if ($entity = $this->getEntity($row, $relationship)) { + $entity_id = $entity->id(); + return $this->build[$entity_id][$this->getLangcodeByRelationship($row, $relationship)]; + } } /** diff --git a/core/modules/views/src/Plugin/views/row/EntityRow.php b/core/modules/views/src/Plugin/views/row/EntityRow.php index ba219f8ae8..862caa421b 100644 --- a/core/modules/views/src/Plugin/views/row/EntityRow.php +++ b/core/modules/views/src/Plugin/views/row/EntityRow.php @@ -235,7 +235,11 @@ public function summaryTitle() { */ public function query() { parent::query(); - $this->getEntityTranslationRenderer()->query($this->view->getQuery()); + $relationship_table = NULL; + if (isset($this->options['relationship'], $this->view->relationship[$this->options['relationship']])) { + $relationship_table = $this->view->relationship[$this->options['relationship']]->alias; + } + $this->getEntityTranslationRenderer()->query($this->view->getQuery(), $relationship_table); } /** @@ -244,7 +248,7 @@ public function query() { public function preRender($result) { parent::preRender($result); if ($result) { - $this->getEntityTranslationRenderer()->preRender($result); + $this->getEntityTranslationRenderer()->preRenderByRelationship($result, isset($this->options['relationship']) ? $this->options['relationship'] : 'none'); } } @@ -252,7 +256,7 @@ public function preRender($result) { * {@inheritdoc} */ public function render($row) { - return $this->getEntityTranslationRenderer()->render($row); + return $this->getEntityTranslationRenderer()->renderByRelationship($row, isset($this->options['relationship']) ? $this->options['relationship'] : 'none'); } /** diff --git a/core/modules/views/src/Plugin/views/row/RowPluginBase.php b/core/modules/views/src/Plugin/views/row/RowPluginBase.php index 3335118504..fd56c2b8db 100644 --- a/core/modules/views/src/Plugin/views/row/RowPluginBase.php +++ b/core/modules/views/src/Plugin/views/row/RowPluginBase.php @@ -90,7 +90,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $data = Views::viewsData()->get($relationship['table']); $base = $data[$relationship['field']]['relationship']['base']; if ($base == $this->base_table) { - $relationship_handler->init($executable, $relationship); + $relationship_handler->init($executable, $this->displayHandler, $relationship); $relationship_options[$relationship['id']] = $relationship_handler->adminLabel(); } } diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml index 7bef8e4a91..d37e2f4bfb 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_row.yml @@ -6,8 +6,8 @@ label: '' module: views description: '' tag: '' -base_table: taxonomy_term_field_data -base_field: nid +base_table: entity_test +base_field: id display: default: display_options: @@ -20,10 +20,17 @@ display: offset: 0 type: none row: - type: 'entity:taxonomy_term' + type: 'entity:entity_test' options: relationship: none view_mode: full + relationships: + user_id: + table: entity_test + field: user_id + id: user_id + relationship: none + plugin_id: standard display_plugin: default display_title: Master id: default diff --git a/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php b/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php index ec1ad3a2aa..7896f19223 100644 --- a/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php +++ b/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php @@ -3,10 +3,10 @@ namespace Drupal\Tests\views\Kernel\Plugin; use Drupal\Core\Form\FormState; +use Drupal\entity_test\Entity\EntityTest; +use Drupal\user\Entity\User; use Drupal\views\Views; use Drupal\Tests\views\Kernel\ViewsKernelTestBase; -use Drupal\taxonomy\Entity\Vocabulary; -use Drupal\taxonomy\Entity\Term; /** * Tests the generic entity row plugin. @@ -22,12 +22,9 @@ class RowEntityTest extends ViewsKernelTestBase { * @var array */ public static $modules = [ - 'taxonomy', - 'text', - 'filter', + 'entity_test', 'field', 'system', - 'node', 'user', ]; @@ -42,27 +39,55 @@ class RowEntityTest extends ViewsKernelTestBase { * {@inheritdoc} */ protected function setUp($import_test_views = TRUE) { - parent::setUp(); + parent::setUp($import_test_views); - $this->installEntitySchema('taxonomy_term'); - $this->installConfig(['taxonomy']); - \Drupal::service('router.builder')->rebuild(); + $this->installEntitySchema('entity_test'); + $this->installEntitySchema('user'); } /** * Tests the entity row handler. */ public function testEntityRow() { - $vocab = Vocabulary::create(['name' => $this->randomMachineName(), 'vid' => strtolower($this->randomMachineName())]); - $vocab->save(); - $term = Term::create(['name' => $this->randomMachineName(), 'vid' => $vocab->id()]); - $term->save(); + $user = User::create([ + 'name' => 'test user', + ]); + $user->save(); + $entity_test = EntityTest::create([ + 'user_id' => $user->id(), + 'name' => 'test entity test', + ]); + $entity_test->save(); + + // Ensure entities have different ids. + if ($entity_test->id() == $user->id()) { + $entity_test->delete(); + $entity_test = EntityTest::create([ + 'user_id' => $user->id(), + 'name' => 'test entity test', + ]); + $entity_test->save(); + } + + $view = Views::getView('test_entity_row'); + $build = $view->preview(); + $this->render($build); + + $this->assertText('test entity test'); + $this->assertNoText('Member for'); + + // Change the view to use a relationship to render the row. $view = Views::getView('test_entity_row'); + $display = &$view->storage->getDisplay('default'); + $display['display_options']['row']['type'] = 'entity:user'; + $display['display_options']['row']['options']['relationship'] = 'user_id'; + $view->setDisplay('default'); $build = $view->preview(); $this->render($build); - $this->assertText($term->getName(), 'The rendered entity appears as row in the view.'); + $this->assertNoText('test entity test'); + $this->assertText('Member for'); // Tests the available view mode options. $form = [];