Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 172
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
EntityViewOverride
0.00% covered (danger)
0.00%
0 / 168
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 22
2756
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getBuilderUrl
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
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 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getUrlFromInstanceId
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 getDisplayUrlFromInstanceId
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
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 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getSources
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 saveSources
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 checkAccess
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 getInstanceId
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 setRevision
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 collectInstances
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getDisplayInfos
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 getRuntimeContexts
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getInitializationMessage
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 getInitialSources
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 collectInstancesByField
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 time
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
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
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 displayBuildableManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 dataConverter
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
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 / 12
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder_entity_view\Plugin\display_builder\Buildable;
6
7use Drupal\Component\Datetime\TimeInterface;
8use Drupal\Core\Access\AccessResult;
9use Drupal\Core\Access\AccessResultInterface;
10use Drupal\Core\Entity\ContentEntityInterface;
11use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
12use Drupal\Core\Entity\Entity\EntityViewDisplay;
13use Drupal\Core\Entity\EntityTypeManagerInterface;
14use Drupal\Core\Entity\Query\QueryInterface;
15use Drupal\Core\Entity\RevisionableEntityBundleInterface;
16use Drupal\Core\Entity\RevisionLogInterface;
17use Drupal\Core\Field\FieldItemListInterface;
18use Drupal\Core\Plugin\Context\Context;
19use Drupal\Core\Plugin\Context\ContextDefinition;
20use Drupal\Core\Plugin\Context\EntityContext;
21use Drupal\Core\Session\AccountInterface;
22use Drupal\Core\StringTranslation\TranslatableMarkup;
23use Drupal\Core\Url;
24use Drupal\display_builder\Attribute\DisplayBuildable;
25use Drupal\display_builder\DisplayBuildableInterface;
26use Drupal\display_builder\DisplayBuildablePluginBase;
27use Drupal\display_builder\DisplayBuildablePluginManager;
28use Drupal\display_builder\Entity\ProfileInterface;
29use Drupal\display_builder_entity_view\BuilderDataConverter;
30use Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface;
31use Drupal\ui_patterns\Plugin\Context\RequirementsContext;
32
33/**
34 * Plugin implementation of the display_buildable.
35 */
36#[DisplayBuildable(
37  id: 'entity_view_override',
38  label: new TranslatableMarkup('Entity view override'),
39  instance_prefix: 'entity_override__',
40)]
41final class EntityViewOverride extends DisplayBuildablePluginBase {
42
43  /**
44   * The time service.
45   */
46  protected ?TimeInterface $time;
47
48  /**
49   * The data converter from Manage Display and Layout Builder.
50   */
51  protected BuilderDataConverter $dataConverter;
52
53  /**
54   * The field items where the override is stored.
55   */
56  protected ?FieldItemListInterface $field = NULL;
57
58  /**
59   * The display buildable plugin manager.
60   */
61  protected DisplayBuildablePluginManager $displayBuildableManager;
62
63  /**
64   * The overridden display.
65   */
66  protected ?DisplayBuilderEntityDisplayInterface $display;
67
68  /**
69   * {@inheritdoc}
70   */
71  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
72    parent::__construct($configuration, $plugin_id, $plugin_definition);
73
74    $this->display = $configuration['display'] ?? NULL;
75
76    if ($this->display === NULL) {
77      // No dependency injection in plugin constructors.
78      /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface|null $display */
79      $display = EntityViewDisplay::load($configuration['display_id']);
80      $this->display = $display;
81    }
82
83    $entity = $configuration['entity'] ?? NULL;
84
85    // The fieldable content entity owning the override field.
86    if ($entity === NULL) {
87      // No dependency injection in plugin constructors.
88      $storage = \Drupal::service('entity_type.manager')->getStorage(
89        $this->display->getTargetEntityTypeId());
90      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
91      $entity = $storage->load($this->configuration['entity_id']);
92    }
93
94    // Configuration to store in the Instance entity.
95    // - display_id (string)
96    // - entity_id (string)
97    unset($this->configuration['display']);
98    $this->configuration['display_id'] = $this->display->id();
99    unset($this->configuration['entity']);
100    $this->configuration['entity_id'] = $entity->id();
101
102    $field_name = $this->display->getDisplayBuilderOverrideField();
103    $this->field = $entity->get($field_name);
104  }
105
106  /**
107   * {@inheritdoc}
108   */
109  public function getBuilderUrl(): Url {
110    \assert(\is_string($this->field->getName()));
111    $entity = $this->field->getEntity();
112    $entity_type_id = $this->display->getTargetEntityTypeId();
113    $parameters = [
114      $entity_type_id => $entity->id(),
115      'view_mode_name' => $this->display->getMode(),
116    ];
117
118    return Url::fromRoute(\sprintf('entity.%s.display_builder.%s', $entity_type_id, $this->display->getMode()), $parameters);
119  }
120
121  /**
122   * {@inheritdoc}
123   */
124  public static function checkInstanceId(string $instance_id): ?array {
125    if (!\str_starts_with($instance_id, self::getPrefix())) {
126      return NULL;
127    }
128    [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id);
129
130    return [
131      'entity_type_id' => $entity_type_id,
132      'entity_id' => $entity_id,
133      'field_name' => $field_name,
134    ];
135  }
136
137  /**
138   * {@inheritdoc}
139   */
140  public static function getUrlFromInstanceId(string $instance_id): Url {
141    [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id);
142
143    $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
144
145    if (!$entity) {
146      return Url::fromRoute('entity.display_builder_instance.collection');
147    }
148
149    $display = self::getEntityViewDisplay($entity_type_id, $entity->bundle(), $field_name);
150
151    if (!$display) {
152      return Url::fromRoute('entity.display_builder_instance.collection');
153    }
154    $params = [
155      $entity_type_id => $entity_id,
156      'view_mode_name' => $display->getMode(),
157    ];
158
159    $route_name = \sprintf('entity.%s.display_builder.%s', $entity_type_id, $display->getMode());
160
161    return Url::fromRoute($route_name, $params);
162  }
163
164  /**
165   * {@inheritdoc}
166   */
167  public static function getDisplayUrlFromInstanceId(string $instance_id): Url {
168    return Url::fromRoute('<front>');
169  }
170
171  /**
172   * {@inheritdoc}
173   */
174  public function getProfile(): ?ProfileInterface {
175    $profile_id = $this->display->getThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY);
176
177    if ($profile_id === NULL) {
178      return NULL;
179    }
180
181    /** @var \Drupal\display_builder\Entity\ProfileInterface $profile */
182    $profile = $this->entityTypeManager->getStorage('display_builder_profile')->load($profile_id);
183
184    return $profile;
185  }
186
187  /**
188   * {@inheritdoc}
189   */
190  public function getSources(): array {
191    return $this->field->getValue();
192  }
193
194  /**
195   * {@inheritdoc}
196   */
197  public function saveSources(): void {
198    $data = $this->getInstance()->getCurrentState();
199    $entity = $this->field->getEntity();
200
201    if ($entity instanceof ContentEntityInterface) {
202      $this->setRevision($entity);
203    }
204    $entity->save();
205    $this->field->setValue($data);
206    $this->field->getEntity()->save();
207  }
208
209  /**
210   * {@inheritdoc}
211   */
212  public static function checkAccess(string $instance_id, AccountInterface $account): AccessResultInterface {
213    [, $entity_type_id, $entity_id] = \explode('__', $instance_id);
214    $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
215
216    if (!$entity) {
217      return AccessResult::neutral();
218    }
219
220    return $entity->access('update', $account, TRUE);
221  }
222
223  /**
224   * {@inheritdoc}
225   */
226  public function getInstanceId(): ?string {
227    // Usually an entity is new if no ID exists for it yet.
228    if ($this->field->getEntity()->isNew()) {
229      return NULL;
230    }
231
232    $entity = $this->field->getEntity();
233
234    return \sprintf(
235      '%s%s__%s__%s',
236      self::getPrefix(),
237      $entity->getEntityTypeId(),
238      $entity->id(),
239      $this->field->getName()
240    );
241  }
242
243  /**
244   * Set revision if appropriate.
245   *
246   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
247   *   The entity to set revision if appropriate.
248   */
249  public function setRevision(ContentEntityInterface $entity): void {
250    $bundle = $entity->getBundleEntity();
251
252    if ($bundle instanceof RevisionableEntityBundleInterface
253      && !$bundle->shouldCreateNewRevision()
254    ) {
255      return;
256    }
257
258    $entity->setNewRevision();
259
260    if ($entity instanceof RevisionLogInterface) {
261      $entity->setRevisionLogMessage($this->t('Updated using Display Builder.')->render());
262      $entity->setRevisionCreationTime($this->time()->getCurrentTime());
263    }
264  }
265
266  /**
267   * {@inheritdoc}
268   */
269  public static function collectInstances(): array {
270    $instances = [];
271    $entityTypeManager = \Drupal::service('entity_type.manager');
272    $storage = $entityTypeManager->getStorage('entity_view_display');
273    $entity_query = $entity_storage = [];
274
275    foreach ($storage->loadMultiple() as $display) {
276      /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
277      $display_builder = $display->getThirdPartySettings('display_builder');
278
279      if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) {
280        continue;
281      }
282
283      $entity_type = $display->getTargetEntityTypeId();
284      $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL;
285
286      if (!$field_name) {
287        continue;
288      }
289      $entity_storage[$entity_type] ??= $entityTypeManager->getStorage($entity_type);
290      $entity_query[$entity_type] ??= $entity_storage[$entity_type]->getQuery()->accessCheck(FALSE);
291      $instances = \array_merge($instances, self::collectInstancesByField($field_name, $display, $entity_query[$entity_type]));
292    }
293
294    return $instances;
295  }
296
297  /**
298   * Gets entity_view_display information grouped by entity type.
299   *
300   * @todo should be replaced by service, see #3542273
301   *
302   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
303   *   The entity type manager service.
304   *
305   * @return array
306   *   An array of display information keyed with 'node', then 'modes' and
307   *   'bundles':
308   *
309   *   @code
310   *   [
311   *     'node' => [
312   *       'modes' => [
313   *         'teaser' => 'Teaser',
314   *       ],
315   *      'bundles' => [
316   *        'article' => [
317   *          'teaser' => 'Teaser',
318   *        ],
319   *      ],
320   *    ],
321   *   ];
322   *
323   *   @endcode
324   */
325  public static function getDisplayInfos(EntityTypeManagerInterface $entityTypeManager): array {
326    /** @var \Drupal\display_builder_entity_view\Entity\EntityViewDisplay[] $displays */
327    $displays = $entityTypeManager
328      ->getStorage('entity_view_display')
329      ->loadMultiple();
330    $view_mode_storage = $entityTypeManager->getStorage('entity_view_mode');
331    $tabs_info = [];
332
333    foreach ($displays as $display) {
334      if (!$display->getDisplayBuilderOverrideField()) {
335        continue;
336      }
337
338      $entity_type_id = $display->getTargetEntityTypeId();
339      $view_mode = $view_mode_storage->load(\sprintf('%s.%s', $entity_type_id, $display->getMode()));
340      $tabs_info[$entity_type_id]['modes'][$display->getMode()] = $view_mode?->label() ?? t('Default');
341      $tabs_info[$entity_type_id]['bundles'][$display->getTargetBundle()][$display->getMode()] = $view_mode?->label() ?? t('Default');
342    }
343
344    return $tabs_info;
345  }
346
347  /**
348   * {@inheritdoc}
349   */
350  public function getRuntimeContexts(array $unqualified_context_ids): array {
351    $contexts = [];
352    $contexts['entity'] = EntityContext::fromEntity($this->field->getEntity());
353    $contexts['view_mode'] = new Context(ContextDefinition::create('string'), $this->display->getMode());
354    $contexts['bundle'] = new Context(ContextDefinition::create('string'), $this->field->getEntity()->bundle());
355    $contexts = RequirementsContext::addToContext(['content'], $contexts);
356
357    return $contexts;
358  }
359
360  /**
361   * {@inheritdoc}
362   */
363  protected function getInitializationMessage(): TranslatableMarkup {
364    if ($this->initialDataSource === 'display_builder') {
365      return $this->t('Copy display from Entity View Display configuration');
366    }
367
368    if ($this->initialDataSource === 'layout_builder_override') {
369      return $this->t('Import display from Layout Builder override');
370    }
371
372    return $this->t('Initialize display from existing content');
373  }
374
375  /**
376   * {@inheritdoc}
377   */
378  protected function getInitialSources(): array {
379    $sources = $this->getSources();
380
381    // 1. Keep the existing override value if existing.
382    if (\count($sources) > 0) {
383      return $sources;
384    }
385
386    // 2. Convert the Layout Builder Override if exists.
387    // There is always a single Layout Builder override per bundle: `default`.
388    // There could be many Display Builder overrides per bundle, one for each
389    // display, so we need to check.
390    if ($this->display->getMode() === 'default') {
391      $entity = $this->field->getEntity();
392      // @see: use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::$FIELD_NAME
393      $field_name = 'layout_builder__layout';
394
395      if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) {
396        $content = $entity->get($field_name)->first()->getValue();
397        $this->initialDataSource = 'layout_builder_override';
398
399        return $this->dataConverter()->convertFromLayoutBuilder($content);
400      }
401    }
402
403    // 3. Copy entity view display value.
404    \assert(\is_string($this->field->getName()));
405    /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */
406    $buildable = $this->displayBuildableManager()->createInstance('entity_view', ['display' => $this->display]);
407
408    if ($buildable->getProfile() !== NULL) {
409      $sources = $buildable->getSources();
410    }
411    $this->initialDataSource = 'display_builder';
412
413    return $sources;
414  }
415
416  /**
417   * Collect instances by field storage.
418   *
419   * @param string $field_name
420   *   Field name.
421   * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
422   *   Entity view display.
423   * @param \Drupal\Core\Entity\Query\QueryInterface $entity_query
424   *   Entity query handler.
425   *
426   * @return \Drupal\display_builder\InstanceInterface[]
427   *   A associative array of Instance entities.
428   */
429  protected static function collectInstancesByField(string $field_name, EntityViewDisplayInterface $display, QueryInterface $entity_query): array {
430    $displayBuildableManager = \Drupal::service('plugin.manager.display_buildable');
431    $instances = [];
432    $entity_query->exists($field_name);
433    // QueryInterface::execute() returns an integer for count queries or an
434    // array of ids.
435    /** @var array $ids */
436    $ids = $entity_query->execute();
437
438    foreach ($ids as $id) {
439      /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */
440      $buildable = $displayBuildableManager->createInstance(
441        'entity_view_override',
442        [
443          'entity_id' => $id,
444          'display' => $display,
445        ]
446      );
447      $buildable->initInstanceIfMissing();
448      $instance = $buildable->getInstance();
449      $instances[$instance->id()] = $instance;
450    }
451
452    return $instances;
453  }
454
455  /**
456   * Get the time service.
457   *
458   * @return \Drupal\Component\Datetime\TimeInterface
459   *   The time service.
460   */
461  protected function time(): TimeInterface {
462    return $this->time ??= \Drupal::service('datetime.time');
463  }
464
465  /**
466   * Get the entity type manager.
467   *
468   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
469   *   The entity type manager.
470   */
471  protected function entityTypeManager(): EntityTypeManagerInterface {
472    return $this->entityTypeManager ??= \Drupal::service('entity_type.manager');
473  }
474
475  /**
476   * Gets the display buildable manager.
477   *
478   * @return \Drupal\display_builder\DisplayBuildablePluginManager
479   *   The manager for display buildable.
480   */
481  protected function displayBuildableManager(): DisplayBuildablePluginManager {
482    return $this->displayBuildableManager ??= \Drupal::service('plugin.manager.display_buildable');
483  }
484
485  /**
486   * Get the data converter from Manage Display and Layout Builder.
487   *
488   * @return \Drupal\display_builder_entity_view\BuilderDataConverter
489   *   The converter.
490   */
491  protected function dataConverter(): BuilderDataConverter {
492    return $this->dataConverter ??= \Drupal::service('display_builder_entity_view.builder_data_converter');
493  }
494
495  /**
496   * Get entity view display entity.
497   *
498   * @param string $entity_type_id
499   *   Entity type ID.
500   * @param string $bundle
501   *   Entity's bundle which support fields.
502   * @param string $fieldName
503   *   Field name of the display.
504   *
505   * @return \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface|null
506   *   The corresponding entity view display.
507   */
508  private static function getEntityViewDisplay(string $entity_type_id, string $bundle, string $fieldName): ?DisplayBuilderEntityDisplayInterface {
509    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */
510    $displays = \Drupal::entityTypeManager()->getStorage('entity_view_display')->loadByProperties([
511      'targetEntityType' => $entity_type_id,
512    ]);
513
514    foreach ($displays as $display) {
515      if ($display instanceof DisplayBuilderEntityDisplayInterface
516        && $display->getDisplayBuilderOverrideField() === $fieldName
517        && $display->getTargetEntityTypeId()
518        && $display->getTargetBundle() === $bundle
519      ) {
520        return $display;
521      }
522    }
523
524    return NULL;
525  }
526
527}

Branches

Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once. Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

EntityViewOverride->__construct
71  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
72    parent::__construct($configuration, $plugin_id, $plugin_definition);
73
74    $this->display = $configuration['display'] ?? NULL;
75
76    if ($this->display === NULL) {
79      $display = EntityViewDisplay::load($configuration['display_id']);
80      $this->display = $display;
81    }
82
83    $entity = $configuration['entity'] ?? NULL;
83    $entity = $configuration['entity'] ?? NULL;
84
85    // The fieldable content entity owning the override field.
86    if ($entity === NULL) {
88      $storage = \Drupal::service('entity_type.manager')->getStorage(
89        $this->display->getTargetEntityTypeId());
90      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
91      $entity = $storage->load($this->configuration['entity_id']);
92    }
93
94    // Configuration to store in the Instance entity.
95    // - display_id (string)
96    // - entity_id (string)
97    unset($this->configuration['display']);
97    unset($this->configuration['display']);
98    $this->configuration['display_id'] = $this->display->id();
99    unset($this->configuration['entity']);
100    $this->configuration['entity_id'] = $entity->id();
101
102    $field_name = $this->display->getDisplayBuilderOverrideField();
103    $this->field = $entity->get($field_name);
104  }
EntityViewOverride->checkAccess
212  public static function checkAccess(string $instance_id, AccountInterface $account): AccessResultInterface {
213    [, $entity_type_id, $entity_id] = \explode('__', $instance_id);
214    $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
215
216    if (!$entity) {
217      return AccessResult::neutral();
220    return $entity->access('update', $account, TRUE);
221  }
EntityViewOverride->checkInstanceId
124  public static function checkInstanceId(string $instance_id): ?array {
125    if (!\str_starts_with($instance_id, self::getPrefix())) {
126      return NULL;
128    [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id);
129
130    return [
131      'entity_type_id' => $entity_type_id,
132      'entity_id' => $entity_id,
133      'field_name' => $field_name,
134    ];
135  }
EntityViewOverride->collectInstances
270    $instances = [];
271    $entityTypeManager = \Drupal::service('entity_type.manager');
272    $storage = $entityTypeManager->getStorage('entity_view_display');
273    $entity_query = $entity_storage = [];
274
275    foreach ($storage->loadMultiple() as $display) {
275    foreach ($storage->loadMultiple() as $display) {
277      $display_builder = $display->getThirdPartySettings('display_builder');
278
279      if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) {
279      if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) {
279      if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) {
280        continue;
283      $entity_type = $display->getTargetEntityTypeId();
284      $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL;
285
286      if (!$field_name) {
287        continue;
275    foreach ($storage->loadMultiple() as $display) {
276      /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
277      $display_builder = $display->getThirdPartySettings('display_builder');
278
279      if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) {
280        continue;
281      }
282
283      $entity_type = $display->getTargetEntityTypeId();
284      $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL;
285
286      if (!$field_name) {
287        continue;
288      }
289      $entity_storage[$entity_type] ??= $entityTypeManager->getStorage($entity_type);
275    foreach ($storage->loadMultiple() as $display) {
276      /** @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display */
277      $display_builder = $display->getThirdPartySettings('display_builder');
278
279      if (!isset($display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY], $display_builder[DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY])) {
280        continue;
281      }
282
283      $entity_type = $display->getTargetEntityTypeId();
284      $field_name = $display_builder[DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY] ?? NULL;
285
286      if (!$field_name) {
287        continue;
288      }
289      $entity_storage[$entity_type] ??= $entityTypeManager->getStorage($entity_type);
290      $entity_query[$entity_type] ??= $entity_storage[$entity_type]->getQuery()->accessCheck(FALSE);
291      $instances = \array_merge($instances, self::collectInstancesByField($field_name, $display, $entity_query[$entity_type]));
292    }
293
294    return $instances;
295  }
EntityViewOverride->collectInstancesByField
429  protected static function collectInstancesByField(string $field_name, EntityViewDisplayInterface $display, QueryInterface $entity_query): array {
430    $displayBuildableManager = \Drupal::service('plugin.manager.display_buildable');
431    $instances = [];
432    $entity_query->exists($field_name);
433    // QueryInterface::execute() returns an integer for count queries or an
434    // array of ids.
435    /** @var array $ids */
436    $ids = $entity_query->execute();
437
438    foreach ($ids as $id) {
438    foreach ($ids as $id) {
438    foreach ($ids as $id) {
439      /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */
440      $buildable = $displayBuildableManager->createInstance(
438    foreach ($ids as $id) {
439      /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */
440      $buildable = $displayBuildableManager->createInstance(
441        'entity_view_override',
442        [
443          'entity_id' => $id,
444          'display' => $display,
445        ]
446      );
447      $buildable->initInstanceIfMissing();
448      $instance = $buildable->getInstance();
449      $instances[$instance->id()] = $instance;
450    }
451
452    return $instances;
453  }
EntityViewOverride->dataConverter
492    return $this->dataConverter ??= \Drupal::service('display_builder_entity_view.builder_data_converter');
493  }
EntityViewOverride->displayBuildableManager
482    return $this->displayBuildableManager ??= \Drupal::service('plugin.manager.display_buildable');
483  }
EntityViewOverride->entityTypeManager
472    return $this->entityTypeManager ??= \Drupal::service('entity_type.manager');
473  }
EntityViewOverride->getBuilderUrl
110    \assert(\is_string($this->field->getName()));
111    $entity = $this->field->getEntity();
112    $entity_type_id = $this->display->getTargetEntityTypeId();
113    $parameters = [
114      $entity_type_id => $entity->id(),
115      'view_mode_name' => $this->display->getMode(),
116    ];
117
118    return Url::fromRoute(\sprintf('entity.%s.display_builder.%s', $entity_type_id, $this->display->getMode()), $parameters);
119  }
EntityViewOverride->getDisplayInfos
325  public static function getDisplayInfos(EntityTypeManagerInterface $entityTypeManager): array {
326    /** @var \Drupal\display_builder_entity_view\Entity\EntityViewDisplay[] $displays */
327    $displays = $entityTypeManager
328      ->getStorage('entity_view_display')
329      ->loadMultiple();
330    $view_mode_storage = $entityTypeManager->getStorage('entity_view_mode');
331    $tabs_info = [];
332
333    foreach ($displays as $display) {
333    foreach ($displays as $display) {
334      if (!$display->getDisplayBuilderOverrideField()) {
335        continue;
333    foreach ($displays as $display) {
334      if (!$display->getDisplayBuilderOverrideField()) {
335        continue;
336      }
337
338      $entity_type_id = $display->getTargetEntityTypeId();
333    foreach ($displays as $display) {
334      if (!$display->getDisplayBuilderOverrideField()) {
335        continue;
336      }
337
338      $entity_type_id = $display->getTargetEntityTypeId();
339      $view_mode = $view_mode_storage->load(\sprintf('%s.%s', $entity_type_id, $display->getMode()));
340      $tabs_info[$entity_type_id]['modes'][$display->getMode()] = $view_mode?->label() ?? t('Default');
341      $tabs_info[$entity_type_id]['bundles'][$display->getTargetBundle()][$display->getMode()] = $view_mode?->label() ?? t('Default');
342    }
343
344    return $tabs_info;
345  }
EntityViewOverride->getDisplayUrlFromInstanceId
167  public static function getDisplayUrlFromInstanceId(string $instance_id): Url {
168    return Url::fromRoute('<front>');
169  }
EntityViewOverride->getEntityViewDisplay
508  private static function getEntityViewDisplay(string $entity_type_id, string $bundle, string $fieldName): ?DisplayBuilderEntityDisplayInterface {
509    /** @var \Drupal\display_builder_entity_view\Entity\DisplayBuilderEntityDisplayInterface[] $displays */
510    $displays = \Drupal::entityTypeManager()->getStorage('entity_view_display')->loadByProperties([
511      'targetEntityType' => $entity_type_id,
512    ]);
513
514    foreach ($displays as $display) {
514    foreach ($displays as $display) {
515      if ($display instanceof DisplayBuilderEntityDisplayInterface
516        && $display->getDisplayBuilderOverrideField() === $fieldName
516        && $display->getDisplayBuilderOverrideField() === $fieldName
517        && $display->getTargetEntityTypeId()
517        && $display->getTargetEntityTypeId()
518        && $display->getTargetBundle() === $bundle
518        && $display->getTargetBundle() === $bundle
520        return $display;
514    foreach ($displays as $display) {
514    foreach ($displays as $display) {
515      if ($display instanceof DisplayBuilderEntityDisplayInterface
516        && $display->getDisplayBuilderOverrideField() === $fieldName
517        && $display->getTargetEntityTypeId()
518        && $display->getTargetBundle() === $bundle
519      ) {
520        return $display;
521      }
522    }
523
524    return NULL;
525  }
EntityViewOverride->getInitialSources
379    $sources = $this->getSources();
380
381    // 1. Keep the existing override value if existing.
382    if (\count($sources) > 0) {
383      return $sources;
390    if ($this->display->getMode() === 'default') {
391      $entity = $this->field->getEntity();
392      // @see: use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::$FIELD_NAME
393      $field_name = 'layout_builder__layout';
394
395      if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) {
395      if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) {
395      if ($entity->hasField($field_name) && !$entity->get($field_name)->isEmpty()) {
396        $content = $entity->get($field_name)->first()->getValue();
397        $this->initialDataSource = 'layout_builder_override';
398
399        return $this->dataConverter()->convertFromLayoutBuilder($content);
404    \assert(\is_string($this->field->getName()));
405    /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */
406    $buildable = $this->displayBuildableManager()->createInstance('entity_view', ['display' => $this->display]);
407
408    if ($buildable->getProfile() !== NULL) {
409      $sources = $buildable->getSources();
410    }
411    $this->initialDataSource = 'display_builder';
411    $this->initialDataSource = 'display_builder';
412
413    return $sources;
414  }
EntityViewOverride->getInitializationMessage
364    if ($this->initialDataSource === 'display_builder') {
365      return $this->t('Copy display from Entity View Display configuration');
368    if ($this->initialDataSource === 'layout_builder_override') {
369      return $this->t('Import display from Layout Builder override');
372    return $this->t('Initialize display from existing content');
373  }
EntityViewOverride->getInstanceId
228    if ($this->field->getEntity()->isNew()) {
229      return NULL;
232    $entity = $this->field->getEntity();
233
234    return \sprintf(
235      '%s%s__%s__%s',
236      self::getPrefix(),
237      $entity->getEntityTypeId(),
238      $entity->id(),
239      $this->field->getName()
240    );
241  }
EntityViewOverride->getProfile
175    $profile_id = $this->display->getThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY);
176
177    if ($profile_id === NULL) {
178      return NULL;
182    $profile = $this->entityTypeManager->getStorage('display_builder_profile')->load($profile_id);
183
184    return $profile;
185  }
EntityViewOverride->getRuntimeContexts
350  public function getRuntimeContexts(array $unqualified_context_ids): array {
351    $contexts = [];
352    $contexts['entity'] = EntityContext::fromEntity($this->field->getEntity());
353    $contexts['view_mode'] = new Context(ContextDefinition::create('string'), $this->display->getMode());
354    $contexts['bundle'] = new Context(ContextDefinition::create('string'), $this->field->getEntity()->bundle());
355    $contexts = RequirementsContext::addToContext(['content'], $contexts);
356
357    return $contexts;
358  }
EntityViewOverride->getSources
191    return $this->field->getValue();
192  }
EntityViewOverride->getUrlFromInstanceId
140  public static function getUrlFromInstanceId(string $instance_id): Url {
141    [, $entity_type_id, $entity_id, $field_name] = \explode('__', $instance_id);
142
143    $entity = \Drupal::entityTypeManager()->getStorage($entity_type_id)->load($entity_id);
144
145    if (!$entity) {
146      return Url::fromRoute('entity.display_builder_instance.collection');
149    $display = self::getEntityViewDisplay($entity_type_id, $entity->bundle(), $field_name);
150
151    if (!$display) {
152      return Url::fromRoute('entity.display_builder_instance.collection');
155      $entity_type_id => $entity_id,
156      'view_mode_name' => $display->getMode(),
157    ];
158
159    $route_name = \sprintf('entity.%s.display_builder.%s', $entity_type_id, $display->getMode());
160
161    return Url::fromRoute($route_name, $params);
162  }
EntityViewOverride->saveSources
198    $data = $this->getInstance()->getCurrentState();
199    $entity = $this->field->getEntity();
200
201    if ($entity instanceof ContentEntityInterface) {
202      $this->setRevision($entity);
203    }
204    $entity->save();
204    $entity->save();
205    $this->field->setValue($data);
206    $this->field->getEntity()->save();
207  }
EntityViewOverride->setRevision
249  public function setRevision(ContentEntityInterface $entity): void {
250    $bundle = $entity->getBundleEntity();
251
252    if ($bundle instanceof RevisionableEntityBundleInterface
253      && !$bundle->shouldCreateNewRevision()
253      && !$bundle->shouldCreateNewRevision()
255      return;
258    $entity->setNewRevision();
259
260    if ($entity instanceof RevisionLogInterface) {
261      $entity->setRevisionLogMessage($this->t('Updated using Display Builder.')->render());
262      $entity->setRevisionCreationTime($this->time()->getCurrentTime());
263    }
264  }
264  }
EntityViewOverride->time
462    return $this->time ??= \Drupal::service('datetime.time');
463  }