EntityReferenceEntityFormatter.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <?php
  2. namespace Drupal\Core\Field\Plugin\Field\FieldFormatter;
  3. use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
  4. use Drupal\Core\Entity\EntityTypeManagerInterface;
  5. use Drupal\Core\Field\FieldDefinitionInterface;
  6. use Drupal\Core\Field\FieldItemListInterface;
  7. use Drupal\Core\Form\FormStateInterface;
  8. use Drupal\Core\Logger\LoggerChannelFactoryInterface;
  9. use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  10. use Symfony\Component\DependencyInjection\ContainerInterface;
  11. /**
  12. * Plugin implementation of the 'entity reference rendered entity' formatter.
  13. *
  14. * @FieldFormatter(
  15. * id = "entity_reference_entity_view",
  16. * label = @Translation("Rendered entity"),
  17. * description = @Translation("Display the referenced entities rendered by entity_view()."),
  18. * field_types = {
  19. * "entity_reference"
  20. * }
  21. * )
  22. */
  23. class EntityReferenceEntityFormatter extends EntityReferenceFormatterBase implements ContainerFactoryPluginInterface {
  24. /**
  25. * The number of times this formatter allows rendering the same entity.
  26. *
  27. * @var int
  28. */
  29. const RECURSIVE_RENDER_LIMIT = 20;
  30. /**
  31. * The logger factory.
  32. *
  33. * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
  34. */
  35. protected $loggerFactory;
  36. /**
  37. * The entity type manager.
  38. *
  39. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  40. */
  41. protected $entityTypeManager;
  42. /**
  43. * The entity display repository.
  44. *
  45. * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
  46. */
  47. protected $entityDisplayRepository;
  48. /**
  49. * An array of counters for the recursive rendering protection.
  50. *
  51. * Each counter takes into account all the relevant information about the
  52. * field and the referenced entity that is being rendered.
  53. *
  54. * @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter::viewElements()
  55. *
  56. * @var array
  57. */
  58. protected static $recursiveRenderDepth = [];
  59. /**
  60. * Constructs a EntityReferenceEntityFormatter instance.
  61. *
  62. * @param string $plugin_id
  63. * The plugin_id for the formatter.
  64. * @param mixed $plugin_definition
  65. * The plugin implementation definition.
  66. * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
  67. * The definition of the field to which the formatter is associated.
  68. * @param array $settings
  69. * The formatter settings.
  70. * @param string $label
  71. * The formatter label display setting.
  72. * @param string $view_mode
  73. * The view mode.
  74. * @param array $third_party_settings
  75. * Any third party settings settings.
  76. * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
  77. * The logger factory.
  78. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  79. * The entity type manager.
  80. * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
  81. * The entity display repository.
  82. */
  83. 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) {
  84. parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
  85. $this->loggerFactory = $logger_factory;
  86. $this->entityTypeManager = $entity_type_manager;
  87. $this->entityDisplayRepository = $entity_display_repository;
  88. }
  89. /**
  90. * {@inheritdoc}
  91. */
  92. public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
  93. return new static(
  94. $plugin_id,
  95. $plugin_definition,
  96. $configuration['field_definition'],
  97. $configuration['settings'],
  98. $configuration['label'],
  99. $configuration['view_mode'],
  100. $configuration['third_party_settings'],
  101. $container->get('logger.factory'),
  102. $container->get('entity_type.manager'),
  103. $container->get('entity_display.repository')
  104. );
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. public static function defaultSettings() {
  110. return [
  111. 'view_mode' => 'default',
  112. 'link' => FALSE,
  113. ] + parent::defaultSettings();
  114. }
  115. /**
  116. * {@inheritdoc}
  117. */
  118. public function settingsForm(array $form, FormStateInterface $form_state) {
  119. $elements['view_mode'] = [
  120. '#type' => 'select',
  121. '#options' => $this->entityDisplayRepository->getViewModeOptions($this->getFieldSetting('target_type')),
  122. '#title' => t('View mode'),
  123. '#default_value' => $this->getSetting('view_mode'),
  124. '#required' => TRUE,
  125. ];
  126. return $elements;
  127. }
  128. /**
  129. * {@inheritdoc}
  130. */
  131. public function settingsSummary() {
  132. $summary = [];
  133. $view_modes = $this->entityDisplayRepository->getViewModeOptions($this->getFieldSetting('target_type'));
  134. $view_mode = $this->getSetting('view_mode');
  135. $summary[] = t('Rendered as @mode', ['@mode' => isset($view_modes[$view_mode]) ? $view_modes[$view_mode] : $view_mode]);
  136. return $summary;
  137. }
  138. /**
  139. * {@inheritdoc}
  140. */
  141. public function viewElements(FieldItemListInterface $items, $langcode) {
  142. $view_mode = $this->getSetting('view_mode');
  143. $elements = [];
  144. foreach ($this->getEntitiesToView($items, $langcode) as $delta => $entity) {
  145. // Due to render caching and delayed calls, the viewElements() method
  146. // will be called later in the rendering process through a '#pre_render'
  147. // callback, so we need to generate a counter that takes into account
  148. // all the relevant information about this field and the referenced
  149. // entity that is being rendered.
  150. $recursive_render_id = $items->getFieldDefinition()->getTargetEntityTypeId()
  151. . $items->getFieldDefinition()->getTargetBundle()
  152. . $items->getName()
  153. // We include the referencing entity, so we can render default images
  154. // without hitting recursive protections.
  155. . $items->getEntity()->id()
  156. . $entity->getEntityTypeId()
  157. . $entity->id();
  158. if (isset(static::$recursiveRenderDepth[$recursive_render_id])) {
  159. static::$recursiveRenderDepth[$recursive_render_id]++;
  160. }
  161. else {
  162. static::$recursiveRenderDepth[$recursive_render_id] = 1;
  163. }
  164. // Protect ourselves from recursive rendering.
  165. if (static::$recursiveRenderDepth[$recursive_render_id] > static::RECURSIVE_RENDER_LIMIT) {
  166. $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.', [
  167. '%entity_type' => $entity->getEntityTypeId(),
  168. '%entity_id' => $entity->id(),
  169. '%field_name' => $items->getName(),
  170. '%bundle_name' => $items->getFieldDefinition()->getTargetBundle(),
  171. ]);
  172. return $elements;
  173. }
  174. $view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());
  175. $elements[$delta] = $view_builder->view($entity, $view_mode, $entity->language()->getId());
  176. // Add a resource attribute to set the mapping property's value to the
  177. // entity's url. Since we don't know what the markup of the entity will
  178. // be, we shouldn't rely on it for structured data such as RDFa.
  179. if (!empty($items[$delta]->_attributes) && !$entity->isNew() && $entity->hasLinkTemplate('canonical')) {
  180. $items[$delta]->_attributes += ['resource' => $entity->toUrl()->toString()];
  181. }
  182. }
  183. return $elements;
  184. }
  185. /**
  186. * {@inheritdoc}
  187. */
  188. public static function isApplicable(FieldDefinitionInterface $field_definition) {
  189. // This formatter is only available for entity types that have a view
  190. // builder.
  191. $target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type');
  192. return \Drupal::entityManager()->getDefinition($target_type)->hasViewBuilderClass();
  193. }
  194. }