relationship = $relationship; $this->entityManager = $entity_manager; } /** * {@inheritdoc} */ public function getCacheContexts() { return $this->getEntityTranslationRenderer()->getCacheContexts(); } /** * {@inheritdoc} */ public function getEntityTypeId() { return $this->entityType->id(); } /** * {@inheritdoc} */ protected function getEntityManager() { return $this->entityManager; } /** * {@inheritdoc} */ protected function getLanguageManager() { return $this->languageManager; } /** * {@inheritdoc} */ protected function getView() { return $this->view; } /** * {@inheritdoc} */ public function query(QueryPluginBase $query, $relationship = NULL) { $this->getEntityTranslationRenderer()->query($query, $relationship); } /** * Renders entity field data. * * @param \Drupal\views\ResultRow $row * A single row of the query result. * @param \Drupal\views\Plugin\views\field\EntityField $field * (optional) A field to be rendered. * * @return array * A renderable array for the entity data contained in the result row. */ public function render(ResultRow $row, EntityField $field = NULL) { // The method is called for each field in each result row. In order to // leverage multiple-entity building of formatter output, we build the // render arrays for all fields in all rows on the first call. if (!isset($this->build)) { $this->build = $this->buildFields($this->view->result); } if (isset($field)) { $field_id = $field->options['id']; // Pick the render array for the row / field we are being asked to render, // and remove it from $this->build to free memory as we progress. if (isset($this->build[$row->index][$field_id])) { $build = $this->build[$row->index][$field_id]; unset($this->build[$row->index][$field_id]); } elseif (isset($this->build[$row->index])) { // In the uncommon case where a field gets rendered several times // (typically through direct Views API calls), the pre-computed render // array was removed by the unset() above. We have to manually rebuild // the render array for the row. $build = $this->buildFields([$row])[$row->index][$field_id]; } else { // In case the relationship is optional, there might not be any fields // to render for this row. $build = []; } } else { // Same logic as above, in the case where we are being called for a whole // row. if (isset($this->build[$row->index])) { $build = $this->build[$row->index]; unset($this->build[$row->index]); } else { $build = $this->buildFields([$row])[$row->index]; } } return $build; } /** * Builds the render arrays for all fields of all result rows. * * The output is built using EntityViewDisplay objects to leverage * multiple-entity building and ensure a common code path with regular entity * view. * - Each relationship is handled by a separate EntityFieldRenderer instance, * since it operates on its own set of entities. This also ensures different * entity types are handled separately, as they imply different * relationships. * - Within each relationship, the fields to render are arranged in unique * sets containing each field at most once (an EntityViewDisplay can * only process a field once with given display options, but a View can * contain the same field several times with different display options). * - For each set of fields, entities are processed by bundle, so that * formatters can operate on the proper field definition for the bundle. * * @param \Drupal\views\ResultRow[] $values * An array of all ResultRow objects returned from the query. * * @return array * A renderable array for the fields handled by this renderer. * * @see \Drupal\Core\Entity\Entity\EntityViewDisplay */ protected function buildFields(array $values) { $build = []; if ($values && ($field_ids = $this->getRenderableFieldIds())) { $entity_type_id = $this->getEntityTypeId(); // Collect the entities for the relationship, fetch the right translation, // and group by bundle. For each result row, the corresponding entity can // be obtained from any of the fields handlers, so we arbitrarily use the // first one. $entities_by_bundles = []; $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); } } // Determine unique sets of fields that can be processed by the same // display. Fields that appear several times in the View open additional // "overflow" displays. $display_sets = []; foreach ($field_ids as $field_id) { $field = $this->view->field[$field_id]; $field_name = $field->definition['field_name']; $index = 0; while (isset($display_sets[$index]['field_names'][$field_name])) { $index++; } $display_sets[$index]['field_names'][$field_name] = $field; $display_sets[$index]['field_ids'][$field_id] = $field; } // For each set of fields, build the output by bundle. foreach ($display_sets as $display_fields) { foreach ($entities_by_bundles as $bundle => $bundle_entities) { // Create the display, and configure the field display options. $display = EntityViewDisplay::create([ 'targetEntityType' => $entity_type_id, 'bundle' => $bundle, 'status' => TRUE, ]); foreach ($display_fields['field_ids'] as $field) { $display->setComponent($field->definition['field_name'], [ 'type' => $field->options['type'], 'settings' => $field->options['settings'], ]); } // Let the display build the render array for the entities. $display_build = $display->buildMultiple($bundle_entities); // Collect the field render arrays and index them using our internal // row indexes and field IDs. foreach ($display_build as $row_index => $entity_build) { foreach ($display_fields['field_ids'] as $field_id => $field) { $build[$row_index][$field_id] = !empty($entity_build[$field->definition['field_name']]) ? $entity_build[$field->definition['field_name']] : []; } } } } } return $build; } /** * Returns a list of names of entity fields to be rendered. * * @return string[] * An associative array of views fields. */ protected function getRenderableFieldIds() { $field_ids = []; foreach ($this->view->field as $field_id => $field) { if ($field instanceof EntityField && $field->relationship == $this->relationship) { $field_ids[] = $field_id; } } return $field_ids; } }