3 namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
5 use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
6 use Drupal\Core\Entity\EntityTypeManagerInterface;
7 use Drupal\Core\Field\FieldDefinitionInterface;
8 use Drupal\Core\Field\FieldItemListInterface;
9 use Drupal\Core\Form\FormStateInterface;
10 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
11 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
15 * Plugin implementation of the 'entity reference rendered entity' formatter.
18 * id = "entity_reference_entity_view",
19 * label = @Translation("Rendered entity"),
20 * description = @Translation("Display the referenced entities rendered by entity_view()."),
26 class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase implements ContainerFactoryPluginInterface {
29 * The number of times this formatter allows rendering the same entity.
33 const RECURSIVE_RENDER_LIMIT = 20;
38 * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
40 protected $loggerFactory;
43 * The entity type manager.
45 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
47 protected $entityTypeManager;
50 * The entity type manager.
52 * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
54 protected $entityDisplayRepository;
57 * An array of counters for the recursive rendering protection.
59 * Each counter takes into account all the relevant information about the
60 * field and the referenced entity that is being rendered.
62 * @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter::viewElements()
66 protected static $recursiveRenderDepth = [];
69 * Constructs a EntityReferenceEntityFormatter instance.
71 * @param string $plugin_id
72 * The plugin_id for the formatter.
73 * @param mixed $plugin_definition
74 * The plugin implementation definition.
75 * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
76 * The definition of the field to which the formatter is associated.
77 * @param array $settings
78 * The formatter settings.
79 * @param string $label
80 * The formatter label display setting.
81 * @param string $view_mode
83 * @param array $third_party_settings
84 * Any third party settings settings.
85 * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
87 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
88 * The entity type manager.
89 * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
90 * The entity display repository.
92 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) {
93 parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
94 $this->loggerFactory = $logger_factory;
95 $this->entityTypeManager = $entity_type_manager;
96 $this->entityDisplayRepository = $entity_display_repository;
102 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
106 $configuration['field_definition'],
107 $configuration['settings'],
108 $configuration['label'],
109 $configuration['view_mode'],
110 $configuration['third_party_settings'],
111 $container->get('logger.factory'),
112 $container->get('entity_type.manager'),
113 $container->get('entity_display.repository')
120 public static function defaultSettings() {
122 'view_mode' => 'default',
124 ] + parent::defaultSettings();
130 public function settingsForm(array $form, FormStateInterface $form_state) {
131 $elements['view_mode'] = [
133 '#options' => $this->entityDisplayRepository->getViewModeOptions($this->getFieldSetting('target_type')),
134 '#title' => t('View mode'),
135 '#default_value' => $this->getSetting('view_mode'),
145 public function settingsSummary() {
148 $view_modes = $this->entityDisplayRepository->getViewModeOptions($this->getFieldSetting('target_type'));
149 $view_mode = $this->getSetting('view_mode');
150 $summary[] = t('Rendered as @mode', ['@mode' => isset($view_modes[$view_mode]) ? $view_modes[$view_mode] : $view_mode]);
158 public function viewElements(FieldItemListInterface $items, $langcode) {
159 $view_mode = $this->getSetting('view_mode');
162 foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
163 // Due to render caching and delayed calls, the viewElements() method
164 // will be called later in the rendering process through a '#pre_render'
165 // callback, so we need to generate a counter that takes into account
166 // all the relevant information about this field and the referenced
167 // entity that is being rendered.
168 $recursive_render_id = $items->getFieldDefinition()->getTargetEntityTypeId()
169 . $items->getFieldDefinition()->getTargetBundle()
171 // We include the referencing entity, so we can render default images
172 // without hitting recursive protections.
173 . $items->getEntity()->id()
174 . $entity->getEntityTypeId()
177 if (isset(static::$recursiveRenderDepth[$recursive_render_id])) {
178 static::$recursiveRenderDepth[$recursive_render_id]++;
181 static::$recursiveRenderDepth[$recursive_render_id] = 1;
184 // Protect ourselves from recursive rendering.
185 if (static::$recursiveRenderDepth[$recursive_render_id] > static::RECURSIVE_RENDER_LIMIT) {
186 $this->loggerFactory->get('entity')->error('Recursive rendering detected when rendering entity %entity_type: %entity_id, using the %field_name field on the %bundle_name bundle. Aborting rendering.', [
187 '%entity_type' => $entity->getEntityTypeId(),
188 '%entity_id' => $entity->id(),
189 '%field_name' => $items->getName(),
190 '%bundle_name' => $items->getFieldDefinition()->getTargetBundle(),
195 $view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());
196 $elements[$delta] = $view_builder->view($entity, $view_mode, $entity->language()->getId());
198 // Add a resource attribute to set the mapping property's value to the
199 // entity's url. Since we don't know what the markup of the entity will
200 // be, we shouldn't rely on it for structured data such as RDFa.
201 if (!empty($items[$delta]->_attributes) && !$entity->isNew() && $entity->hasLinkTemplate('canonical')) {
202 $items[$delta]->_attributes += ['resource' => $entity->toUrl()->toString()];
212 public static function isApplicable(FieldDefinitionInterface $field_definition) {
213 // This formatter is only available for entity types that have a view
215 $target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type');
216 return \Drupal::entityManager()->getDefinition($target_type)->hasViewBuilderClass();