Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 98
0.00% covered (danger)
0.00%
0 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
DisplayBuilderItemList
0.00% covered (danger)
0.00%
0 / 98
0.00% covered (danger)
0.00%
0 / 16
870
0.00% covered (danger)
0.00%
0 / 1
 getPrefix
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getContextRequirement
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getBuilderUrl
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
 checkInstanceId
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 getUrlFromInstanceId
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 getDisplayUrlFromInstanceId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getProfile
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 getInstanceId
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 getSources
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 saveSources
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 initInstanceIfMissing
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getInitialSources
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 getInitialContext
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 entityTypeManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEntityViewDisplay
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
42
 getInstance
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder_entity_view\Field;
6
7use Drupal\Core\Entity\EntityTypeManagerInterface;
8use Drupal\Core\Field\MapFieldItemList;
9use Drupal\Core\Plugin\Context\Context;
10use Drupal\Core\Plugin\Context\ContextDefinition;
11use Drupal\Core\Plugin\Context\EntityContext;
12use Drupal\Core\Url;
13use Drupal\display_builder\DisplayBuildableInterface;
14use Drupal\display_builder\InstanceInterface;
15use Drupal\display_builder\ProfileInterface;
16use Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface;
17use Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface;
18use Drupal\ui_patterns\Plugin\Context\RequirementsContext;
19
20/**
21 * Defines an item list class for layout section fields.
22 *
23 * @internal
24 *   Plugin classes are internal.
25 *
26 * @see \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem
27 *
28 * phpcs:disable DrupalPractice.Objects.GlobalDrupal.GlobalDrupal
29 */
30final class DisplayBuilderItemList extends MapFieldItemList implements DisplayBuildableInterface {
31
32  /**
33   * The entity type manager.
34   */
35  protected ?EntityTypeManagerInterface $entityTypeManager;
36
37  /**
38   * The loaded display builder instance.
39   */
40  protected ?InstanceInterface $instance;
41
42  /**
43   * {@inheritdoc}
44   */
45  public static function getPrefix(): string {
46    return 'entity_override__';
47  }
48
49  /**
50   * {@inheritdoc}
51   */
52  public static function getContextRequirement(): string {
53    return 'content';
54  }
55
56  /**
57   * {@inheritdoc}
58   */
59  public function getBuilderUrl(): Url {
60    \assert(\is_string($this->getName()));
61    $entity = $this->getEntity();
62    $entity_view = self::getEntityViewDisplay(
63      $entity->getEntityTypeId(),
64      $entity->bundle(),
65      $this->getName(),
66    );
67    $entity_type_id = $entity_view->getTargetEntityTypeId();
68    $parameters = [
69      $entity_type_id => $entity->id(),
70      'view_mode_name' => $entity_view->getMode(),
71    ];
72
73    return Url::fromRoute("entity.{$entity_type_id}.display_builder.{$entity_view->getMode()}", $parameters);
74  }
75
76  /**
77   * {@inheritdoc}
78   */
79  public static function checkInstanceId(string $instance_id): ?array {
80    if (!\str_starts_with($instance_id, self::getPrefix())) {
81      return NULL;
82    }
83    [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id);
84
85    return [
86      'entity_type_id' => $entity_type_id,
87      'entity_id' => $entity_id,
88      'field_name' => $field_name,
89    ];
90  }
91
92  /**
93   * {@inheritdoc}
94   */
95  public static function getUrlFromInstanceId(string $instance_id): Url {
96    [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id);
97
98    $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
99    $display = self::getEntityViewDisplay($entity_type_id, $entity->bundle(), $field_name);
100    $params = [
101      $entity_type_id => $entity_id,
102      'view_mode_name' => $display->getMode(),
103    ];
104
105    $route_name = \sprintf('entity.%s.display_builder.%s', $entity_type_id, $display->getMode());
106
107    return Url::fromRoute($route_name, $params);
108  }
109
110  /**
111   * {@inheritdoc}
112   */
113  public static function getDisplayUrlFromInstanceId(string $instance_id): Url {
114    return Url::fromRoute('<front>');
115  }
116
117  /**
118   * {@inheritdoc}
119   */
120  public function getProfile(): ?ProfileInterface {
121    \assert(\is_string($this->getName()));
122    $entity = $this->getEntity();
123
124    $entity_view = self::getEntityViewDisplay(
125      $entity->getEntityTypeId(),
126      $entity->bundle(),
127      $this->getName(),
128    );
129    \assert($entity_view instanceof DisplayBuilderOverridableInterface);
130
131    return $entity_view->getDisplayBuilderOverrideProfile();
132  }
133
134  /**
135   * {@inheritdoc}
136   */
137  public function getInstanceId(): ?string {
138    // Usually an entity is new if no ID exists for it yet.
139    if ($this->getEntity()->isNew()) {
140      return NULL;
141    }
142
143    $entity = $this->getEntity();
144
145    return \sprintf('%s%s__%s__%s',
146      self::getPrefix(),
147      $entity->getEntityTypeId(),
148      $entity->id(),
149      $this->getName()
150    );
151  }
152
153  /**
154   * {@inheritdoc}
155   */
156  public function getSources(): array {
157    $data = [];
158
159    foreach ($this->list as $offset => $item) {
160      $data[$offset] = $item->getValue();
161    }
162
163    return $data;
164  }
165
166  /**
167   * {@inheritdoc}
168   */
169  public function saveSources(): void {
170    $data = $this->getInstance()->getCurrentState();
171    $this->list = [];
172
173    foreach ($data as $offset => $item) {
174      $this->list[$offset] = $this->createItem($offset, $item);
175    }
176    $this->getEntity()->save();
177  }
178
179  /**
180   * {@inheritdoc}
181   */
182  public function initInstanceIfMissing(): void {
183    /** @var \Drupal\display_builder\InstanceStorage $storage */
184    $storage = $this->entityTypeManager()->getStorage('display_builder_instance');
185
186    /** @var \Drupal\display_builder\InstanceInterface $instance */
187    $instance = $storage->load($this->getInstanceId());
188
189    if (!$instance) {
190      $instance = $storage->createFromImplementation($this);
191      $instance->save();
192    }
193  }
194
195  /**
196   * {@inheritdoc}
197   */
198  public function getInitialSources(): array {
199    $sources = $this->getSources();
200    $entity = $this->getEntity();
201
202    if (\count($sources) === 0) {
203      \assert(\is_string($this->getName()));
204      $display = self::getEntityViewDisplay($entity->getEntityTypeId(), $entity->bundle(), $this->getName());
205
206      if ($display->getProfile() !== NULL) {
207        $sources = $display->getSources();
208      }
209    }
210
211    return $sources;
212  }
213
214  /**
215   * {@inheritdoc}
216   */
217  public function getInitialContext(): array {
218    $entity = $this->getEntity();
219    $bundle = $entity->bundle();
220    \assert(\is_string($this->getName()));
221
222    $view_mode = self::getEntityViewDisplay($entity->getEntityTypeId(), $bundle, $this->getName())->getMode();
223    $contexts = [
224      'entity' => EntityContext::fromEntity($entity),
225      'bundle' => new Context(ContextDefinition::create('string'), $bundle),
226      'view_mode' => new Context(ContextDefinition::create('string'), $view_mode),
227    ];
228
229    return RequirementsContext::addToContext([self::getContextRequirement()], $contexts);
230  }
231
232  /**
233   * Get the entity type manager.
234   *
235   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
236   *   The entity type manager.
237   */
238  protected function entityTypeManager(): EntityTypeManagerInterface {
239    return $this->entityTypeManager ??= \Drupal::service('entity_type.manager');
240  }
241
242  /**
243   * Get entity view display entity.
244   *
245   * @param string $entity_type_id
246   *   Entity type ID.
247   * @param string $bundle
248   *   Entity's bundle which support fields.
249   * @param string $fieldName
250   *   Field name of the display.
251   *
252   * @return \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface|null
253   *   The corresponding entity view display.
254   */
255  private static function getEntityViewDisplay(string $entity_type_id, string $bundle, string $fieldName): ?DisplayBuilderEntityDisplayInterface {
256    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */
257    $displays = \Drupal::entityTypeManager()->getStorage('entity_view_display')->loadByProperties([
258      'targetEntityType' => $entity_type_id,
259    ]);
260
261    foreach ($displays as $display) {
262      if ($display instanceof DisplayBuilderOverridableInterface
263        && $display->getDisplayBuilderOverrideField() === $fieldName
264        && $display->getTargetEntityTypeId()
265        && $display->getTargetBundle() === $bundle
266      ) {
267        return $display;
268      }
269    }
270
271    return NULL;
272  }
273
274  /**
275   * Gets the Display Builder instance.
276   *
277   * @return \Drupal\display_builder\InstanceInterface|null
278   *   A display builder instance.
279   */
280  private function getInstance(): ?InstanceInterface {
281    if (!isset($this->instance)) {
282      /** @var \Drupal\display_builder\InstanceInterface|null $instance */
283      $instance = $this->entityTypeManager()->getStorage('display_builder_instance')->load($this->getInstanceId());
284      $this->instance = $instance;
285    }
286
287    return $this->instance;
288  }
289
290}