You are here

class EntityFieldRenderer in Zircon Profile 8

Same name and namespace in other branches
  1. 8.0 core/modules/views/src/Entity/Render/EntityFieldRenderer.php \Drupal\views\Entity\Render\EntityFieldRenderer

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.

Hierarchy

Expanded class hierarchy of EntityFieldRenderer

1 file declares its use of EntityFieldRenderer
Field.php in core/modules/views/src/Plugin/views/field/Field.php
Contains \Drupal\views\Plugin\views\field\Field.

File

core/modules/views/src/Entity/Render/EntityFieldRenderer.php, line 26
Contains \Drupal\views\Entity\Render\EntityFieldRenderer.

Namespace

Drupal\views\Entity\Render
View source
class EntityFieldRenderer extends RendererBase {
  use EntityTranslationRenderTrait;

  /**
   * 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\Field $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, Field $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 Field && $field->relationship == $this->relationship) {
        $field_ids[] = $field_id;
      }
    }
    return $field_ids;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
EntityFieldRenderer::$entityManager protected property The entity manager.
EntityFieldRenderer::$processedRows protected property A list of indexes of rows whose fields have already been rendered.
EntityFieldRenderer::$relationship protected property The relationship being handled.
EntityFieldRenderer::buildFields protected function Builds the render arrays for all fields of all result rows.
EntityFieldRenderer::getCacheContexts public function The cache contexts associated with this object. Overrides RendererBase::getCacheContexts
EntityFieldRenderer::getEntityManager protected function Returns the entity manager. Overrides EntityTranslationRenderTrait::getEntityManager
EntityFieldRenderer::getEntityTypeId public function Returns the entity type identifier. Overrides EntityTranslationRenderTrait::getEntityTypeId
EntityFieldRenderer::getLanguageManager protected function Returns the language manager. Overrides EntityTranslationRenderTrait::getLanguageManager
EntityFieldRenderer::getRenderableFieldIds protected function Returns a list of names of entity fields to be rendered.
EntityFieldRenderer::getView protected function Returns the top object of a view. Overrides EntityTranslationRenderTrait::getView
EntityFieldRenderer::query public function Alters the query if needed. Overrides RendererBase::query
EntityFieldRenderer::render public function Renders entity field data. Overrides RendererBase::render
EntityFieldRenderer::__construct public function Constructs an EntityFieldRenderer object. Overrides RendererBase::__construct
EntityTranslationRenderTrait::$entityTranslationRenderer protected property The renderer to be used to render the entity row.
EntityTranslationRenderTrait::getEntityTranslation public function Returns the entity translation matching the configured row language.
EntityTranslationRenderTrait::getEntityTranslationRenderer protected function Returns the current renderer.
RendererBase::$build protected property Contains an array of render arrays, one for each rendered entity.
RendererBase::$entityType protected property The type of the entity being rendered.
RendererBase::$languageManager protected property The language manager.
RendererBase::$view public property The view executable wrapping the view storage entity.
RendererBase::getCacheMaxAge public function The maximum age for which this object may be cached. Overrides CacheableDependencyInterface::getCacheMaxAge
RendererBase::getCacheTags public function The cache tags associated with this object. Overrides CacheableDependencyInterface::getCacheTags
RendererBase::preRender public function Runs before each entity is rendered. 1