3 namespace Drupal\layout_builder\Plugin\Block;
5 use Drupal\Core\Block\BlockBase;
6 use Drupal\Core\Cache\CacheableMetadata;
7 use Drupal\Core\Entity\EntityFieldManagerInterface;
8 use Drupal\Core\Entity\EntityTypeManagerInterface;
9 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10 use Drupal\Core\Plugin\ContextAwarePluginInterface;
11 use Drupal\Core\Render\Element;
12 use Drupal\Core\Session\AccountInterface;
13 use Drupal\Core\StringTranslation\TranslatableMarkup;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
17 * Provides a block that renders an extra field from an entity.
19 * This block handles fields that are provided by implementations of
20 * hook_entity_extra_field_info().
22 * @see \Drupal\layout_builder\Plugin\Block\FieldBlock
23 * This block plugin handles all other field entities not provided by
24 * hook_entity_extra_field_info().
27 * id = "extra_field_block",
28 * deriver = "\Drupal\layout_builder\Plugin\Derivative\ExtraFieldBlockDeriver",
32 * Plugin classes are internal.
34 class ExtraFieldBlock extends BlockBase implements ContextAwarePluginInterface, ContainerFactoryPluginInterface {
37 * The entity field manager.
39 * @var \Drupal\Core\Entity\EntityFieldManagerInterface
41 protected $entityFieldManager;
51 * The entity type manager.
53 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
55 protected $entityTypeManager;
58 * Constructs a new ExtraFieldBlock.
60 * @param array $configuration
61 * A configuration array containing information about the plugin instance.
62 * @param string $plugin_id
63 * The plugin ID for the plugin instance.
64 * @param mixed $plugin_definition
65 * The plugin implementation definition.
66 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
67 * The entity type manager.
68 * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
69 * The entity field manager.
71 public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
72 $this->entityTypeManager = $entity_type_manager;
73 $this->entityFieldManager = $entity_field_manager;
74 // Get field name from the plugin ID.
75 list (, , , $field_name) = explode(static::DERIVATIVE_SEPARATOR, $plugin_id, 4);
76 assert(!empty($field_name));
77 $this->fieldName = $field_name;
79 parent::__construct($configuration, $plugin_id, $plugin_definition);
85 public function defaultConfiguration() {
87 'label_display' => FALSE,
94 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
99 $container->get('entity_type.manager'),
100 $container->get('entity_field.manager')
105 * Gets the entity that has the field.
107 * @return \Drupal\Core\Entity\FieldableEntityInterface
110 protected function getEntity() {
111 return $this->getContextValue('entity');
117 public function build() {
118 $entity = $this->getEntity();
119 // Add a placeholder to replace after the entity view is built.
120 // @see layout_builder_entity_view_alter().
121 $extra_fields = $this->entityFieldManager->getExtraFields($entity->getEntityTypeId(), $entity->bundle());
122 if (!isset($extra_fields['display'][$this->fieldName])) {
127 '#extra_field_placeholder_field_name' => $this->fieldName,
128 // Always provide a placeholder. The Layout Builder will NOT invoke
129 // hook_entity_view_alter() so extra fields will not be added to the
130 // render array. If the hook is invoked the placeholder will be
132 // @see ::replaceFieldPlaceholder()
133 '#markup' => new TranslatableMarkup('Placeholder for the "@field" field', ['@field' => $extra_fields['display'][$this->fieldName]['label']]),
136 CacheableMetadata::createFromObject($this)->applyTo($build);
141 * Replaces all placeholders for a given field.
143 * @param array $build
144 * The built render array for the elements.
145 * @param array $built_field
146 * The render array to replace the placeholder.
147 * @param string $field_name
152 public static function replaceFieldPlaceholder(array &$build, array $built_field, $field_name) {
153 foreach (Element::children($build) as $child) {
154 if (isset($build[$child]['#extra_field_placeholder_field_name']) && $build[$child]['#extra_field_placeholder_field_name'] === $field_name) {
155 $placeholder_cache = CacheableMetadata::createFromRenderArray($build[$child]);
156 $built_cache = CacheableMetadata::createFromRenderArray($built_field);
157 $merged_cache = $placeholder_cache->merge($built_cache);
158 $build[$child] = $built_field;
159 $merged_cache->applyTo($build);
162 static::replaceFieldPlaceholder($build[$child], $built_field, $field_name);
170 protected function blockAccess(AccountInterface $account) {
171 return $this->getEntity()->access('view', $account, TRUE);