EntityReferenceEntityFormatter.php

Same filename in other branches
  1. 9 core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php
  2. 8.9.x core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php
  3. 10 core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php

Namespace

Drupal\Core\Field\Plugin\Field\FieldFormatter

File

core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceEntityFormatter.php

View source
<?php

namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;

use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\Attribute\FieldFormatter;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Plugin implementation of the 'entity reference rendered entity' formatter.
 */
class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase {
    
    /**
     * The number of times this formatter allows rendering the same entity.
     *
     * @var int
     */
    const RECURSIVE_RENDER_LIMIT = 20;
    
    /**
     * The logger factory.
     *
     * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
     */
    protected $loggerFactory;
    
    /**
     * The entity type manager.
     *
     * @var \Drupal\Core\Entity\EntityTypeManagerInterface
     */
    protected $entityTypeManager;
    
    /**
     * The entity display repository.
     *
     * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
     */
    protected $entityDisplayRepository;
    
    /**
     * An array of counters for the recursive rendering protection.
     *
     * Each counter takes into account all the relevant information about the
     * field and the referenced entity that is being rendered.
     *
     * @var array
     *
     * @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter::viewElements()
     */
    protected static $recursiveRenderDepth = [];
    
    /**
     * Constructs an EntityReferenceEntityFormatter instance.
     *
     * @param string $plugin_id
     *   The plugin ID for the formatter.
     * @param mixed $plugin_definition
     *   The plugin implementation definition.
     * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
     *   The definition of the field to which the formatter is associated.
     * @param array $settings
     *   The formatter settings.
     * @param string $label
     *   The formatter label display setting.
     * @param string $view_mode
     *   The view mode.
     * @param array $third_party_settings
     *   Any third party settings.
     * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
     *   The logger factory.
     * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
     *   The entity type manager.
     * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
     *   The entity display repository.
     */
    public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, LoggerChannelFactoryInterface $logger_factory, EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository) {
        parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
        $this->loggerFactory = $logger_factory;
        $this->entityTypeManager = $entity_type_manager;
        $this->entityDisplayRepository = $entity_display_repository;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
        return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'], $container->get('logger.factory'), $container->get('entity_type.manager'), $container->get('entity_display.repository'));
    }
    
    /**
     * {@inheritdoc}
     */
    public static function defaultSettings() {
        return [
            'view_mode' => 'default',
            'link' => FALSE,
        ] + parent::defaultSettings();
    }
    
    /**
     * {@inheritdoc}
     */
    public function settingsForm(array $form, FormStateInterface $form_state) {
        $elements['view_mode'] = [
            '#type' => 'select',
            '#options' => $this->entityDisplayRepository
                ->getViewModeOptions($this->getFieldSetting('target_type')),
            '#title' => $this->t('View mode'),
            '#default_value' => $this->getSetting('view_mode'),
            '#required' => TRUE,
        ];
        return $elements;
    }
    
    /**
     * {@inheritdoc}
     */
    public function settingsSummary() {
        $summary = [];
        $view_modes = $this->entityDisplayRepository
            ->getViewModeOptions($this->getFieldSetting('target_type'));
        $view_mode = $this->getSetting('view_mode');
        $summary[] = $this->t('Rendered as @mode', [
            '@mode' => $view_modes[$view_mode] ?? $view_mode,
        ]);
        return $summary;
    }
    
    /**
     * {@inheritdoc}
     */
    public function viewElements(FieldItemListInterface $items, $langcode) {
        $view_mode = $this->getSetting('view_mode');
        $elements = [];
        foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
            // Due to render caching and delayed calls, the viewElements() method
            // will be called later in the rendering process through a '#pre_render'
            // callback, so we need to generate a counter that takes into account
            // all the relevant information about this field and the referenced
            // entity that is being rendered.
            $recursive_render_id = $items->getFieldDefinition()
                ->getTargetEntityTypeId() . $items->getFieldDefinition()
                ->getTargetBundle() . $items->getName() . $items->getEntity()
                ->id() . $entity->getEntityTypeId() . $entity->id();
            if (isset(static::$recursiveRenderDepth[$recursive_render_id])) {
                static::$recursiveRenderDepth[$recursive_render_id]++;
            }
            else {
                static::$recursiveRenderDepth[$recursive_render_id] = 1;
            }
            // Protect ourselves from recursive rendering.
            if (static::$recursiveRenderDepth[$recursive_render_id] > static::RECURSIVE_RENDER_LIMIT) {
                $this->loggerFactory
                    ->get('entity')
                    ->error('Recursive rendering detected when rendering entity %entity_type: %entity_id, using the %field_name field on the %parent_entity_type:%parent_bundle %parent_entity_id entity. Aborting rendering.', [
                    '%entity_type' => $entity->getEntityTypeId(),
                    '%entity_id' => $entity->id(),
                    '%field_name' => $items->getName(),
                    '%parent_entity_type' => $items->getFieldDefinition()
                        ->getTargetEntityTypeId(),
                    '%parent_bundle' => $items->getFieldDefinition()
                        ->getTargetBundle(),
                    '%parent_entity_id' => $items->getEntity()
                        ->id(),
                ]);
                return $elements;
            }
            $view_builder = $this->entityTypeManager
                ->getViewBuilder($entity->getEntityTypeId());
            $elements[$delta] = $view_builder->view($entity, $view_mode, $entity->language()
                ->getId());
            // Add a resource attribute to set the mapping property's value to the
            // entity's URL. Since we don't know what the markup of the entity will
            // be, we shouldn't rely on it for structured data.
            if (!empty($items[$delta]->_attributes) && !$entity->isNew() && $entity->hasLinkTemplate('canonical')) {
                $items[$delta]->_attributes += [
                    'resource' => $entity->toUrl()
                        ->toString(),
                ];
            }
        }
        return $elements;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function isApplicable(FieldDefinitionInterface $field_definition) {
        // This formatter is only available for entity types that have a view
        // builder.
        $target_type = $field_definition->getFieldStorageDefinition()
            ->getSetting('target_type');
        return \Drupal::entityTypeManager()->getDefinition($target_type)
            ->hasViewBuilderClass();
    }

}

Classes

Title Deprecated Summary
EntityReferenceEntityFormatter Plugin implementation of the 'entity reference rendered entity' formatter.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.