Mercurial > hg > isophonics-drupal-site
diff core/modules/views/src/Entity/Render/EntityFieldRenderer.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | af1871eacc83 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,269 @@ +<?php + +namespace Drupal\views\Entity\Render; + +use Drupal\Core\DependencyInjection\DependencySerializationTrait; +use Drupal\Core\Entity\Entity\EntityViewDisplay; +use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\views\Plugin\views\field\EntityField; +use Drupal\views\Plugin\views\query\QueryPluginBase; +use Drupal\views\ResultRow; +use Drupal\views\ViewExecutable; + +/** + * Renders entity fields. + * + * This is used to build render arrays for all entity field values of a view + * result set sharing the same relationship. An entity translation renderer is + * used internally to handle entity language properly. + */ +class EntityFieldRenderer extends RendererBase { + use EntityTranslationRenderTrait; + use DependencySerializationTrait; + + /** + * The relationship being handled. + * + * @var string + */ + protected $relationship; + + /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** + * A list of indexes of rows whose fields have already been rendered. + * + * @var int[] + */ + protected $processedRows = []; + + /** + * Constructs an EntityFieldRenderer object. + * + * @param \Drupal\views\ViewExecutable $view + * The view whose fields are being rendered. + * @param string $relationship + * The relationship to be handled. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + */ + public function __construct(ViewExecutable $view, $relationship, LanguageManagerInterface $language_manager, EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager) { + parent::__construct($view, $language_manager, $entity_type); + $this->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; + } + +}