diff --git a/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php b/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php index 2e28f9d..eeea694 100644 --- a/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php +++ b/core/modules/views/src/Entity/Render/ConfigurableLanguageRenderer.php @@ -39,7 +39,7 @@ public function __construct(ViewExecutable $view, LanguageManagerInterface $lang /** * {@inheritdoc} */ - public function getLangcode(ResultRow $row) { + public function getLangcode(ResultRow $row, $relationship = 'none') { return $this->langcode; } diff --git a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php index 11dbb4a..63b86b1 100644 --- a/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php +++ b/core/modules/views/src/Entity/Render/DefaultLanguageRenderer.php @@ -2,18 +2,9 @@ namespace Drupal\views\Entity\Render; -use Drupal\views\ResultRow; - /** * Renders entities in their default language. */ class DefaultLanguageRenderer extends EntityTranslationRendererBase { - /** - * {@inheritdoc} - */ - public function getLangcode(ResultRow $row) { - return $row->_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 1b05c1b..93a3dd4 100644 --- a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php +++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php @@ -200,7 +200,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->getEntityTranslation($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 e08ef04..cdbe3ad 100644 --- a/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php +++ b/core/modules/views/src/Entity/Render/EntityTranslationRenderTrait.php @@ -62,18 +62,20 @@ protected function getEntityTranslationRenderer() { * 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 getEntityTranslation(EntityInterface $entity, ResultRow $row) { + public function getEntityTranslation(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()->getLangcode($row, $relationship); $translation = $this->getEntityManager()->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 e77992d..2dc495e 100644 --- a/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php +++ b/core/modules/views/src/Entity/Render/EntityTranslationRendererBase.php @@ -15,11 +15,17 @@ * * @param \Drupal\views\ResultRow $row * The result row. + * @param string $relationship + * The relationship to be used, or 'none' by default. * * @return string * A language code. */ - abstract public function getLangcode(ResultRow $row); + public function getLangcode(ResultRow $row, $relationship = 'none') { + if ($entity = $this->getEntity($row, $relationship)) { + return $entity->getUntranslated()->language()->getId(); + } + } /** * {@inheritdoc} @@ -28,27 +34,62 @@ public function query(QueryPluginBase $query, $relationship = NULL) { } /** - * {@inheritdoc} + * Runs before each entity is rendered. + * + * @param \Drupal\views\ResultRow[] $result + * The full array of results from the query. + * @param string $relationship + * The relationship to be used, or 'none' by default. */ - public function preRender(array $result) { + public function preRender(array $result, $relationship = 'none') { $view_builder = $this->view->rowPlugin->entityManager->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->getLangcode($row, $relationship)); + } } } /** - * {@inheritdoc} + * Renders entity data. + * + * @param \Drupal\views\ResultRow $row + * A single row of the query result. + * @param string $relationship + * The relationship to be used, or 'none' by default. + * + * @return array + * A renderable array for the entity data contained in the result row. */ - public function render(ResultRow $row) { - $entity_id = $row->_entity->id(); - return $this->build[$entity_id]; + public function render(ResultRow $row, $relationship = 'none') { + if ($entity = $this->getEntity($row, $relationship)) { + $entity_id = $entity->id(); + return $this->build[$entity_id]; + } + } + + /** + * Gets the entity assosiated 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 60327f6..b47dd43 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 00ae524..1d78c9d 100644 --- a/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php +++ b/core/modules/views/src/Entity/Render/TranslationLanguageRenderer.php @@ -40,31 +40,34 @@ public function query(QueryPluginBase $query, $relationship = NULL) { /** * {@inheritdoc} */ - public function preRender(array $result) { + public function preRender(array $result, $relationship = 'none') { $view_builder = $this->view->rowPlugin->entityManager->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->getLangcode($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 render(ResultRow $row, $relationship = 'none') { + if ($entity = $this->getEntity($row, $relationship)) { + $entity_id = $entity->id(); + $langcode = $this->getLangcode($row, $relationship); + return $this->build[$entity_id][$langcode]; + } } /** * {@inheritdoc} */ - public function getLangcode(ResultRow $row) { + public function getLangcode(ResultRow $row, $relationship = 'none') { return isset($row->{$this->langcodeAlias}) ? $row->{$this->langcodeAlias} : $this->languageManager->getDefaultLanguage()->getId(); } diff --git a/core/modules/views/src/Plugin/views/row/EntityRow.php b/core/modules/views/src/Plugin/views/row/EntityRow.php index f536529..d54e032 100644 --- a/core/modules/views/src/Plugin/views/row/EntityRow.php +++ b/core/modules/views/src/Plugin/views/row/EntityRow.php @@ -166,7 +166,13 @@ public function summaryTitle() { */ public function query() { parent::query(); - $this->getEntityTranslationRenderer()->query($this->view->getQuery()); + if (isset($this->options['relationship'], $this->view->relationship[$this->options['relationship']])) { + $relationship = $this->view->relationship[$this->options['relationship']]->alias; + } + else { + $relationship = NULL; + } + $this->getEntityTranslationRenderer()->query($this->view->getQuery(), $relationship); } /** @@ -175,7 +181,7 @@ public function query() { public function preRender($result) { parent::preRender($result); if ($result) { - $this->getEntityTranslationRenderer()->preRender($result); + $this->getEntityTranslationRenderer()->preRender($result, isset($this->options['relationship']) ? $this->options['relationship'] : 'none'); } } @@ -183,7 +189,7 @@ public function preRender($result) { * {@inheritdoc} */ public function render($row) { - return $this->getEntityTranslationRenderer()->render($row); + return $this->getEntityTranslationRenderer()->render($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 d1e090e..a60070b 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 ad9f748..c786e20 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 core: '8' display: default: @@ -21,10 +21,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 8d2d866..0236b59 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. @@ -21,7 +21,7 @@ class RowEntityTest extends ViewsKernelTestBase { * * @var array */ - public static $modules = ['taxonomy', 'text', 'filter', 'field', 'system', 'node', 'user']; + public static $modules = ['entity_test', 'field', 'system', 'user']; /** * Views used by this test. @@ -34,27 +34,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 = [];