Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
50.94% |
54 / 106 |
|
53.33% |
40 / 75 |
|
24.29% |
17 / 70 |
|
37.50% |
6 / 16 |
CRAP | |
0.00% |
0 / 1 |
| EntityViewDisplayTrait | |
50.94% |
54 / 106 |
|
53.33% |
40 / 75 |
|
24.29% |
17 / 70 |
|
37.50% |
6 / 16 |
845.55 | |
0.00% |
0 / 1 |
| calculateDependencies | |
92.31% |
12 / 13 |
|
90.00% |
9 / 10 |
|
50.00% |
3 / 6 |
|
0.00% |
0 / 1 |
8.12 | |||
| isDisplayBuilderEnabled | |
100.00% |
3 / 3 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| onDependencyRemoval | |
91.67% |
11 / 12 |
|
87.50% |
7 / 8 |
|
20.00% |
1 / 5 |
|
0.00% |
0 / 1 |
12.19 | |||
| getDisplayBuilderOverrideField | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getDisplayBuilderOverrideProfile | |
100.00% |
4 / 4 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| isDisplayBuilderOverridable | |
100.00% |
2 / 2 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| postSave | |
61.11% |
11 / 18 |
|
62.50% |
10 / 16 |
|
10.00% |
3 / 30 |
|
0.00% |
0 / 1 |
54.66 | |||
| delete | |
0.00% |
0 / 3 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| buildMultiple | |
0.00% |
0 / 15 |
|
0.00% |
0 / 10 |
|
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
30 | |||
| isOverrideOfCurrentDisplay | |
0.00% |
0 / 8 |
|
0.00% |
0 / 7 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
20 | |||
| getContextsForEntity | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| contextRepository | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getInstance | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| displayBuildable | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| loadDisplayBuilder | |
80.00% |
4 / 5 |
|
66.67% |
2 / 3 |
|
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
2.50 | |||
| buildSources | |
0.00% |
0 / 9 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder_entity_view\Entity; |
| 6 | |
| 7 | use Drupal\Core\Cache\CacheableMetadata; |
| 8 | use Drupal\Core\Entity\EntityStorageInterface; |
| 9 | use Drupal\Core\Entity\FieldableEntityInterface; |
| 10 | use Drupal\Core\Plugin\Context\Context; |
| 11 | use Drupal\Core\Plugin\Context\ContextDefinition; |
| 12 | use Drupal\Core\Plugin\Context\ContextRepositoryInterface; |
| 13 | use Drupal\Core\Plugin\Context\EntityContext; |
| 14 | use Drupal\display_builder\DisplayBuildableInterface; |
| 15 | use Drupal\display_builder\InstanceInterface; |
| 16 | use Drupal\display_builder\ProfileInterface; |
| 17 | use Drupal\display_builder_entity_view\Plugin\display_builder\Buildable\EntityViewOverride; |
| 18 | |
| 19 | /** |
| 20 | * Common methods for entity view display. |
| 21 | */ |
| 22 | trait EntityViewDisplayTrait { |
| 23 | |
| 24 | /** |
| 25 | * Calculates dependencies for the display builder. |
| 26 | * |
| 27 | * @return $this |
| 28 | * The current instance. |
| 29 | * |
| 30 | * @see \Drupal\Core\Entity\Display\EntityViewDisplayInterface |
| 31 | */ |
| 32 | public function calculateDependencies(): self { |
| 33 | parent::calculateDependencies(); |
| 34 | |
| 35 | if (!$this->displayBuildable()->getInstanceId()) { |
| 36 | // If there is no instance ID, we cannot calculate dependencies. |
| 37 | return $this; |
| 38 | } |
| 39 | |
| 40 | /** @var \Drupal\display_builder\InstanceInterface $instance */ |
| 41 | $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($this->displayBuildable()->getInstanceId()); |
| 42 | |
| 43 | if (!$instance) { |
| 44 | return $this; |
| 45 | } |
| 46 | $contexts = $instance->getContexts(); |
| 47 | |
| 48 | if (!$contexts) { |
| 49 | return $this; |
| 50 | } |
| 51 | |
| 52 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 53 | /** @var \Drupal\ui_patterns\SourceInterface $source */ |
| 54 | $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts); |
| 55 | $this->addDependencies($source->calculateDependencies()); |
| 56 | } |
| 57 | |
| 58 | return $this; |
| 59 | } |
| 60 | |
| 61 | /** |
| 62 | * Is display builder enabled? |
| 63 | * |
| 64 | * @return bool |
| 65 | * The display builder is enabled if there is a Display Builder entity. |
| 66 | * |
| 67 | * @see \Drupal\display_builder_entity_view\DisplayBuilderEntityDisplayInterface |
| 68 | */ |
| 69 | public function isDisplayBuilderEnabled(): bool { |
| 70 | // Display Builder must not be enabled for the '_custom' view mode that is |
| 71 | // used for on-the-fly rendering of fields in isolation from the entity. |
| 72 | if ($this->getOriginalMode() === static::CUSTOM_MODE) { |
| 73 | return FALSE; |
| 74 | } |
| 75 | |
| 76 | return (bool) $this->displayBuildable()->getProfile(); |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Handler for when dependencies are removed. |
| 81 | * |
| 82 | * @param array $dependencies |
| 83 | * The dependencies that were removed. |
| 84 | * |
| 85 | * @return bool |
| 86 | * TRUE if the display can be overridden, FALSE otherwise. |
| 87 | * |
| 88 | * @see \Drupal\Core\Entity\Display\EntityViewDisplayInterface |
| 89 | */ |
| 90 | public function onDependencyRemoval(array $dependencies): bool { |
| 91 | $changed = parent::onDependencyRemoval($dependencies); |
| 92 | |
| 93 | // Loop through all sources and determine if the removed dependencies are |
| 94 | // used by their plugins. |
| 95 | /** @var \Drupal\display_builder\InstanceInterface $instance */ |
| 96 | $instance = $this->getInstance(); |
| 97 | |
| 98 | // @todo not working when content entity type is deleted. |
| 99 | if (!$instance) { |
| 100 | return TRUE; |
| 101 | } |
| 102 | |
| 103 | $contexts = $instance->getContexts(); |
| 104 | |
| 105 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 106 | /** @var \Drupal\ui_patterns\SourceInterface $source */ |
| 107 | $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts); |
| 108 | $source_dependencies = $source->calculateDependencies(); |
| 109 | $source_removed_dependencies = $this->getPluginRemovedDependencies($source_dependencies, $dependencies); |
| 110 | |
| 111 | if ($source_removed_dependencies) { |
| 112 | // @todo Allow the plugins to react to their dependency removal in |
| 113 | // https://www.drupal.org/project/drupal/issues/2579743. |
| 114 | // $this->removeSource($delta); |
| 115 | $changed = TRUE; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | return $changed; |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Returns the field name used to store overridden displays. |
| 124 | * |
| 125 | * @return string|null |
| 126 | * The field name used to store overridden displays, or NULL if not set. |
| 127 | * |
| 128 | * @see \Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface |
| 129 | */ |
| 130 | public function getDisplayBuilderOverrideField(): ?string { |
| 131 | return $this->getThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY); |
| 132 | } |
| 133 | |
| 134 | /** |
| 135 | * Returns the display builder override profile. |
| 136 | * |
| 137 | * @return \Drupal\display_builder\ProfileInterface|null |
| 138 | * The display builder override profile, or NULL if not set. |
| 139 | * |
| 140 | * @see \Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface |
| 141 | */ |
| 142 | public function getDisplayBuilderOverrideProfile(): ?ProfileInterface { |
| 143 | $display_builder_id = $this->getThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY); |
| 144 | |
| 145 | if ($display_builder_id === NULL) { |
| 146 | return NULL; |
| 147 | } |
| 148 | |
| 149 | return $this->loadDisplayBuilder($display_builder_id); |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | * Returns TRUE if the display can be overridden. |
| 154 | * |
| 155 | * @return bool |
| 156 | * TRUE if the display can be overridden, FALSE otherwise. |
| 157 | * |
| 158 | * @see \Drupal\display_builder_entity_view\Entity\DisplayBuilderOverridableInterface |
| 159 | */ |
| 160 | public function isDisplayBuilderOverridable(): bool { |
| 161 | return !empty($this->getDisplayBuilderOverrideField()) |
| 162 | && $this->getDisplayBuilderOverrideProfile() !== NULL; |
| 163 | } |
| 164 | |
| 165 | /** |
| 166 | * Post-save operations for the display builder. |
| 167 | * |
| 168 | * @param \Drupal\Core\Entity\EntityStorageInterface $storage |
| 169 | * The entity storage. |
| 170 | * @param bool $update |
| 171 | * Whether the entity is being updated. |
| 172 | * |
| 173 | * @see \Drupal\Core\Entity\Display\EntityViewDisplayInterface |
| 174 | */ |
| 175 | public function postSave(EntityStorageInterface $storage, $update = TRUE): void { |
| 176 | if ($profile = $this->displayBuildable()->getProfile()) { |
| 177 | $this->displayBuildable()->initInstanceIfMissing(); |
| 178 | |
| 179 | // Save the profile in the instance if changed. |
| 180 | $instance = $this->getInstance(); |
| 181 | $profile_id = (string) $profile->id(); |
| 182 | |
| 183 | if ($instance && ($instance->getProfile()?->id() !== $profile_id)) { |
| 184 | $instance->setProfile($profile_id); |
| 185 | } |
| 186 | $instance->save(); |
| 187 | } |
| 188 | |
| 189 | // Do also overrides. |
| 190 | if ($profile = $this->getDisplayBuilderOverrideProfile()) { |
| 191 | $profile_id = (string) $profile->id(); |
| 192 | $storage = $this->entityTypeManager->getStorage('display_builder_instance'); |
| 193 | |
| 194 | foreach ($storage->loadMultiple() as $override) { |
| 195 | /** @var \Drupal\display_builder\InstanceInterface $override */ |
| 196 | if (!$this->isOverrideOfCurrentDisplay($override)) { |
| 197 | continue; |
| 198 | } |
| 199 | |
| 200 | if ($override->getProfile()->id() === $profile_id) { |
| 201 | continue; |
| 202 | } |
| 203 | $override->setProfile($profile_id); |
| 204 | $override->save(); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | parent::postSave($storage, $update); |
| 209 | } |
| 210 | |
| 211 | /** |
| 212 | * Deletes the display builder instance if it exists. |
| 213 | * |
| 214 | * @see \Drupal\Core\Entity\Display\EntityViewDisplayInterface |
| 215 | */ |
| 216 | public function delete(): void { |
| 217 | if ($instance = $this->getInstance()) { |
| 218 | $instance->delete(); |
| 219 | } |
| 220 | parent::delete(); |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Builds a renderable array for the components of a set of entities. |
| 225 | * |
| 226 | * @param \Drupal\Core\Entity\FieldableEntityInterface[] $entities |
| 227 | * The entities being displayed. |
| 228 | * |
| 229 | * @return array |
| 230 | * A renderable array for the entities, indexed by the same keys as the |
| 231 | * $entities array parameter. |
| 232 | * |
| 233 | * @see \Drupal\Core\Entity\Display\EntityViewDisplayInterface |
| 234 | */ |
| 235 | public function buildMultiple(array $entities): array { |
| 236 | $build_list = parent::buildMultiple($entities); |
| 237 | |
| 238 | // If no display builder enabled, stop here and return: |
| 239 | // - 'Manage Display' build if this trait is used in EntityViewDisplay |
| 240 | // - 'Layout Builder' build if used in LayoutBuilderEntityViewDisplay. |
| 241 | if (!$this->isDisplayBuilderEnabled()) { |
| 242 | // This is also preventing the availability of Display Builder overrides |
| 243 | // when Display Builder is not used for the entity view display. |
| 244 | // @todo Is it something we want to keep like that? |
| 245 | // @see https://www.drupal.org/project/display_builder/issues/3540048 |
| 246 | return $build_list; |
| 247 | } |
| 248 | |
| 249 | foreach ($entities as $id => $entity) { |
| 250 | $sources = []; |
| 251 | |
| 252 | if ($this->isDisplayBuilderOverridable()) { |
| 253 | $display_builder_field = $this->getDisplayBuilderOverrideField(); |
| 254 | $overridden_field = $entity->get($display_builder_field); |
| 255 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 256 | $buildable = $this->displayBuildableManager->createInstance('entity_view_override', ['field' => $overridden_field]); |
| 257 | $sources = $buildable->getSources(); |
| 258 | } |
| 259 | |
| 260 | // If the overridden field is empty fallback to the entity view. |
| 261 | if (\count($sources) === 0) { |
| 262 | $sources = $this->displayBuildable()->getSources(); |
| 263 | } |
| 264 | |
| 265 | // We clear the display because we only want our renderable. |
| 266 | $build_list[$id] = []; |
| 267 | // @see entity.html.twig |
| 268 | $build_list[$id]['content'] = $this->buildSources($entity, $sources); |
| 269 | } |
| 270 | |
| 271 | return $build_list; |
| 272 | } |
| 273 | |
| 274 | /** |
| 275 | * Check if the instance is overriding this display. |
| 276 | * |
| 277 | * @param \Drupal\display_builder\InstanceInterface $instance |
| 278 | * A list of display builder instances. |
| 279 | * |
| 280 | * @return bool |
| 281 | * Is the instance overriding this display? |
| 282 | */ |
| 283 | protected function isOverrideOfCurrentDisplay(InstanceInterface $instance): bool { |
| 284 | $parts = EntityViewOverride::checkInstanceId((string) $instance->id()); |
| 285 | |
| 286 | if (!$parts) { |
| 287 | return FALSE; |
| 288 | } |
| 289 | |
| 290 | if ($parts['entity_type_id'] !== $this->getTargetEntityTypeId()) { |
| 291 | return FALSE; |
| 292 | } |
| 293 | |
| 294 | if ($parts['field_name'] !== $this->getDisplayBuilderOverrideField()) { |
| 295 | return FALSE; |
| 296 | } |
| 297 | |
| 298 | return TRUE; |
| 299 | } |
| 300 | |
| 301 | /** |
| 302 | * Gets the available contexts for a given entity. |
| 303 | * |
| 304 | * @param \Drupal\Core\Entity\FieldableEntityInterface $entity |
| 305 | * The entity. |
| 306 | * |
| 307 | * @return \Drupal\Core\Plugin\Context\ContextInterface[] |
| 308 | * An array of context objects for a given entity. |
| 309 | */ |
| 310 | protected function getContextsForEntity(FieldableEntityInterface $entity): array { |
| 311 | $available_context_ids = \array_keys($this->contextRepository()->getAvailableContexts()); |
| 312 | |
| 313 | return [ |
| 314 | 'view_mode' => new Context(ContextDefinition::create('string'), $this->getMode()), |
| 315 | 'entity' => EntityContext::fromEntity($entity), |
| 316 | 'display' => EntityContext::fromEntity($this), |
| 317 | ] + $this->contextRepository()->getRuntimeContexts($available_context_ids); |
| 318 | } |
| 319 | |
| 320 | /** |
| 321 | * Wraps the context repository service. |
| 322 | * |
| 323 | * @return \Drupal\Core\Plugin\Context\ContextRepositoryInterface |
| 324 | * The context repository service. |
| 325 | */ |
| 326 | protected function contextRepository(): ContextRepositoryInterface { |
| 327 | return \Drupal::service('context.repository'); |
| 328 | } |
| 329 | |
| 330 | /** |
| 331 | * Gets the Display Builder instance. |
| 332 | * |
| 333 | * @return \Drupal\display_builder\InstanceInterface|null |
| 334 | * A display builder instance. |
| 335 | */ |
| 336 | protected function getInstance(): ?InstanceInterface { |
| 337 | $instance_id = $this->displayBuildable()->getInstanceId(); |
| 338 | /** @var \Drupal\display_builder\InstanceInterface|null $instance */ |
| 339 | $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($instance_id); |
| 340 | $this->instance = $instance; |
| 341 | |
| 342 | return $this->instance; |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Gets the display buildable manager. |
| 347 | * |
| 348 | * @return \Drupal\display_builder\DisplayBuildableInterface |
| 349 | * The manager for display buildable. |
| 350 | */ |
| 351 | protected function displayBuildable(): DisplayBuildableInterface { |
| 352 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 353 | $buildable = $this->displayBuildableManager->createInstance('entity_view', ['entity' => $this]); |
| 354 | |
| 355 | return $buildable; |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * Loads display builder by id. |
| 360 | * |
| 361 | * @param string $display_builder_id |
| 362 | * The display builder ID. |
| 363 | * |
| 364 | * @return \Drupal\display_builder\ProfileInterface|null |
| 365 | * The display builder, or NULL if not found. |
| 366 | */ |
| 367 | private function loadDisplayBuilder(string $display_builder_id): ?ProfileInterface { |
| 368 | if (empty($display_builder_id)) { |
| 369 | return NULL; |
| 370 | } |
| 371 | $storage = $this->entityTypeManager->getStorage('display_builder_profile'); |
| 372 | |
| 373 | /** @var \Drupal\display_builder\ProfileInterface $display_builder */ |
| 374 | $display_builder = $storage->load($display_builder_id); |
| 375 | |
| 376 | return $display_builder; |
| 377 | } |
| 378 | |
| 379 | /** |
| 380 | * Builds the render array for the sources of a given entity. |
| 381 | * |
| 382 | * @param \Drupal\Core\Entity\FieldableEntityInterface $entity |
| 383 | * The entity. |
| 384 | * @param array $sources |
| 385 | * The sources to build. |
| 386 | * |
| 387 | * @return array |
| 388 | * The render array representing the sources of the entity. |
| 389 | */ |
| 390 | private function buildSources(FieldableEntityInterface $entity, array $sources): array { |
| 391 | $contexts = $this->getContextsForEntity($entity); |
| 392 | $cacheability = new CacheableMetadata(); |
| 393 | $fake_build = []; |
| 394 | |
| 395 | foreach ($sources as $source_data) { |
| 396 | $fake_build = $this->componentElementBuilder->buildSource($fake_build, 'content', [], $source_data, $contexts); |
| 397 | } |
| 398 | $build = $fake_build['#slots']['content'] ?? []; |
| 399 | $build['#cache'] = $fake_build['#cache'] ?? []; |
| 400 | // The render array is built based on decisions made by SourceStorage |
| 401 | // plugins, and therefore it needs to depend on the accumulated |
| 402 | // cacheability of those decisions. |
| 403 | $cacheability->applyTo($build); |
| 404 | |
| 405 | return $build; |
| 406 | } |
| 407 | |
| 408 | } |
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.
| 235 | public function buildMultiple(array $entities): array { |
| 236 | $build_list = parent::buildMultiple($entities); |
| 237 | |
| 238 | // If no display builder enabled, stop here and return: |
| 239 | // - 'Manage Display' build if this trait is used in EntityViewDisplay |
| 240 | // - 'Layout Builder' build if used in LayoutBuilderEntityViewDisplay. |
| 241 | if (!$this->isDisplayBuilderEnabled()) { |
| 246 | return $build_list; |
| 249 | foreach ($entities as $id => $entity) { |
| 249 | foreach ($entities as $id => $entity) { |
| 249 | foreach ($entities as $id => $entity) { |
| 250 | $sources = []; |
| 251 | |
| 252 | if ($this->isDisplayBuilderOverridable()) { |
| 253 | $display_builder_field = $this->getDisplayBuilderOverrideField(); |
| 254 | $overridden_field = $entity->get($display_builder_field); |
| 255 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 256 | $buildable = $this->displayBuildableManager->createInstance('entity_view_override', ['field' => $overridden_field]); |
| 257 | $sources = $buildable->getSources(); |
| 258 | } |
| 259 | |
| 260 | // If the overridden field is empty fallback to the entity view. |
| 261 | if (\count($sources) === 0) { |
| 261 | if (\count($sources) === 0) { |
| 262 | $sources = $this->displayBuildable()->getSources(); |
| 263 | } |
| 264 | |
| 265 | // We clear the display because we only want our renderable. |
| 266 | $build_list[$id] = []; |
| 249 | foreach ($entities as $id => $entity) { |
| 250 | $sources = []; |
| 251 | |
| 252 | if ($this->isDisplayBuilderOverridable()) { |
| 253 | $display_builder_field = $this->getDisplayBuilderOverrideField(); |
| 254 | $overridden_field = $entity->get($display_builder_field); |
| 255 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 256 | $buildable = $this->displayBuildableManager->createInstance('entity_view_override', ['field' => $overridden_field]); |
| 257 | $sources = $buildable->getSources(); |
| 258 | } |
| 259 | |
| 260 | // If the overridden field is empty fallback to the entity view. |
| 261 | if (\count($sources) === 0) { |
| 262 | $sources = $this->displayBuildable()->getSources(); |
| 263 | } |
| 264 | |
| 265 | // We clear the display because we only want our renderable. |
| 266 | $build_list[$id] = []; |
| 249 | foreach ($entities as $id => $entity) { |
| 250 | $sources = []; |
| 251 | |
| 252 | if ($this->isDisplayBuilderOverridable()) { |
| 253 | $display_builder_field = $this->getDisplayBuilderOverrideField(); |
| 254 | $overridden_field = $entity->get($display_builder_field); |
| 255 | /** @var \Drupal\display_builder\DisplayBuildableInterface $buildable */ |
| 256 | $buildable = $this->displayBuildableManager->createInstance('entity_view_override', ['field' => $overridden_field]); |
| 257 | $sources = $buildable->getSources(); |
| 258 | } |
| 259 | |
| 260 | // If the overridden field is empty fallback to the entity view. |
| 261 | if (\count($sources) === 0) { |
| 262 | $sources = $this->displayBuildable()->getSources(); |
| 263 | } |
| 264 | |
| 265 | // We clear the display because we only want our renderable. |
| 266 | $build_list[$id] = []; |
| 267 | // @see entity.html.twig |
| 268 | $build_list[$id]['content'] = $this->buildSources($entity, $sources); |
| 269 | } |
| 270 | |
| 271 | return $build_list; |
| 272 | } |
| 390 | private function buildSources(FieldableEntityInterface $entity, array $sources): array { |
| 391 | $contexts = $this->getContextsForEntity($entity); |
| 392 | $cacheability = new CacheableMetadata(); |
| 393 | $fake_build = []; |
| 394 | |
| 395 | foreach ($sources as $source_data) { |
| 395 | foreach ($sources as $source_data) { |
| 395 | foreach ($sources as $source_data) { |
| 396 | $fake_build = $this->componentElementBuilder->buildSource($fake_build, 'content', [], $source_data, $contexts); |
| 395 | foreach ($sources as $source_data) { |
| 396 | $fake_build = $this->componentElementBuilder->buildSource($fake_build, 'content', [], $source_data, $contexts); |
| 397 | } |
| 398 | $build = $fake_build['#slots']['content'] ?? []; |
| 399 | $build['#cache'] = $fake_build['#cache'] ?? []; |
| 400 | // The render array is built based on decisions made by SourceStorage |
| 401 | // plugins, and therefore it needs to depend on the accumulated |
| 402 | // cacheability of those decisions. |
| 403 | $cacheability->applyTo($build); |
| 404 | |
| 405 | return $build; |
| 406 | } |
| 33 | parent::calculateDependencies(); |
| 34 | |
| 35 | if (!$this->displayBuildable()->getInstanceId()) { |
| 37 | return $this; |
| 41 | $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($this->displayBuildable()->getInstanceId()); |
| 42 | |
| 43 | if (!$instance) { |
| 44 | return $this; |
| 46 | $contexts = $instance->getContexts(); |
| 47 | |
| 48 | if (!$contexts) { |
| 49 | return $this; |
| 52 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 52 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 52 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 53 | /** @var \Drupal\ui_patterns\SourceInterface $source */ |
| 54 | $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts); |
| 52 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 53 | /** @var \Drupal\ui_patterns\SourceInterface $source */ |
| 54 | $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts); |
| 55 | $this->addDependencies($source->calculateDependencies()); |
| 56 | } |
| 57 | |
| 58 | return $this; |
| 59 | } |
| 327 | return \Drupal::service('context.repository'); |
| 328 | } |
| 217 | if ($instance = $this->getInstance()) { |
| 218 | $instance->delete(); |
| 219 | } |
| 220 | parent::delete(); |
| 220 | parent::delete(); |
| 221 | } |
| 353 | $buildable = $this->displayBuildableManager->createInstance('entity_view', ['entity' => $this]); |
| 354 | |
| 355 | return $buildable; |
| 356 | } |
| 310 | protected function getContextsForEntity(FieldableEntityInterface $entity): array { |
| 311 | $available_context_ids = \array_keys($this->contextRepository()->getAvailableContexts()); |
| 312 | |
| 313 | return [ |
| 314 | 'view_mode' => new Context(ContextDefinition::create('string'), $this->getMode()), |
| 315 | 'entity' => EntityContext::fromEntity($entity), |
| 316 | 'display' => EntityContext::fromEntity($this), |
| 317 | ] + $this->contextRepository()->getRuntimeContexts($available_context_ids); |
| 318 | } |
| 131 | return $this->getThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_FIELD_PROPERTY); |
| 132 | } |
| 143 | $display_builder_id = $this->getThirdPartySetting('display_builder', DisplayBuildableInterface::OVERRIDE_PROFILE_PROPERTY); |
| 144 | |
| 145 | if ($display_builder_id === NULL) { |
| 146 | return NULL; |
| 149 | return $this->loadDisplayBuilder($display_builder_id); |
| 150 | } |
| 337 | $instance_id = $this->displayBuildable()->getInstanceId(); |
| 338 | /** @var \Drupal\display_builder\InstanceInterface|null $instance */ |
| 339 | $instance = $this->entityTypeManager->getStorage('display_builder_instance')->load($instance_id); |
| 340 | $this->instance = $instance; |
| 341 | |
| 342 | return $this->instance; |
| 343 | } |
| 72 | if ($this->getOriginalMode() === static::CUSTOM_MODE) { |
| 73 | return FALSE; |
| 76 | return (bool) $this->displayBuildable()->getProfile(); |
| 77 | } |
| 161 | return !empty($this->getDisplayBuilderOverrideField()) |
| 162 | && $this->getDisplayBuilderOverrideProfile() !== NULL; |
| 162 | && $this->getDisplayBuilderOverrideProfile() !== NULL; |
| 163 | } |
| 283 | protected function isOverrideOfCurrentDisplay(InstanceInterface $instance): bool { |
| 284 | $parts = EntityViewOverride::checkInstanceId((string) $instance->id()); |
| 285 | |
| 286 | if (!$parts) { |
| 287 | return FALSE; |
| 290 | if ($parts['entity_type_id'] !== $this->getTargetEntityTypeId()) { |
| 291 | return FALSE; |
| 294 | if ($parts['field_name'] !== $this->getDisplayBuilderOverrideField()) { |
| 295 | return FALSE; |
| 298 | return TRUE; |
| 299 | } |
| 367 | private function loadDisplayBuilder(string $display_builder_id): ?ProfileInterface { |
| 368 | if (empty($display_builder_id)) { |
| 369 | return NULL; |
| 371 | $storage = $this->entityTypeManager->getStorage('display_builder_profile'); |
| 372 | |
| 373 | /** @var \Drupal\display_builder\ProfileInterface $display_builder */ |
| 374 | $display_builder = $storage->load($display_builder_id); |
| 375 | |
| 376 | return $display_builder; |
| 377 | } |
| 90 | public function onDependencyRemoval(array $dependencies): bool { |
| 91 | $changed = parent::onDependencyRemoval($dependencies); |
| 92 | |
| 93 | // Loop through all sources and determine if the removed dependencies are |
| 94 | // used by their plugins. |
| 95 | /** @var \Drupal\display_builder\InstanceInterface $instance */ |
| 96 | $instance = $this->getInstance(); |
| 97 | |
| 98 | // @todo not working when content entity type is deleted. |
| 99 | if (!$instance) { |
| 100 | return TRUE; |
| 103 | $contexts = $instance->getContexts(); |
| 104 | |
| 105 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 105 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 107 | $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts); |
| 108 | $source_dependencies = $source->calculateDependencies(); |
| 109 | $source_removed_dependencies = $this->getPluginRemovedDependencies($source_dependencies, $dependencies); |
| 110 | |
| 111 | if ($source_removed_dependencies) { |
| 105 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 106 | /** @var \Drupal\ui_patterns\SourceInterface $source */ |
| 107 | $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts); |
| 108 | $source_dependencies = $source->calculateDependencies(); |
| 109 | $source_removed_dependencies = $this->getPluginRemovedDependencies($source_dependencies, $dependencies); |
| 110 | |
| 111 | if ($source_removed_dependencies) { |
| 112 | // @todo Allow the plugins to react to their dependency removal in |
| 113 | // https://www.drupal.org/project/drupal/issues/2579743. |
| 114 | // $this->removeSource($delta); |
| 115 | $changed = TRUE; |
| 105 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 105 | foreach ($this->displayBuildable()->getSources() as $source_data) { |
| 106 | /** @var \Drupal\ui_patterns\SourceInterface $source */ |
| 107 | $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts); |
| 108 | $source_dependencies = $source->calculateDependencies(); |
| 109 | $source_removed_dependencies = $this->getPluginRemovedDependencies($source_dependencies, $dependencies); |
| 110 | |
| 111 | if ($source_removed_dependencies) { |
| 112 | // @todo Allow the plugins to react to their dependency removal in |
| 113 | // https://www.drupal.org/project/drupal/issues/2579743. |
| 114 | // $this->removeSource($delta); |
| 115 | $changed = TRUE; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | return $changed; |
| 120 | } |
| 175 | public function postSave(EntityStorageInterface $storage, $update = TRUE): void { |
| 176 | if ($profile = $this->displayBuildable()->getProfile()) { |
| 177 | $this->displayBuildable()->initInstanceIfMissing(); |
| 178 | |
| 179 | // Save the profile in the instance if changed. |
| 180 | $instance = $this->getInstance(); |
| 181 | $profile_id = (string) $profile->id(); |
| 182 | |
| 183 | if ($instance && ($instance->getProfile()?->id() !== $profile_id)) { |
| 183 | if ($instance && ($instance->getProfile()?->id() !== $profile_id)) { |
| 183 | if ($instance && ($instance->getProfile()?->id() !== $profile_id)) { |
| 184 | $instance->setProfile($profile_id); |
| 185 | } |
| 186 | $instance->save(); |
| 186 | $instance->save(); |
| 187 | } |
| 188 | |
| 189 | // Do also overrides. |
| 190 | if ($profile = $this->getDisplayBuilderOverrideProfile()) { |
| 190 | if ($profile = $this->getDisplayBuilderOverrideProfile()) { |
| 191 | $profile_id = (string) $profile->id(); |
| 192 | $storage = $this->entityTypeManager->getStorage('display_builder_instance'); |
| 193 | |
| 194 | foreach ($storage->loadMultiple() as $override) { |
| 194 | foreach ($storage->loadMultiple() as $override) { |
| 196 | if (!$this->isOverrideOfCurrentDisplay($override)) { |
| 197 | continue; |
| 200 | if ($override->getProfile()->id() === $profile_id) { |
| 201 | continue; |
| 194 | foreach ($storage->loadMultiple() as $override) { |
| 195 | /** @var \Drupal\display_builder\InstanceInterface $override */ |
| 196 | if (!$this->isOverrideOfCurrentDisplay($override)) { |
| 197 | continue; |
| 198 | } |
| 199 | |
| 200 | if ($override->getProfile()->id() === $profile_id) { |
| 201 | continue; |
| 202 | } |
| 203 | $override->setProfile($profile_id); |
| 194 | foreach ($storage->loadMultiple() as $override) { |
| 195 | /** @var \Drupal\display_builder\InstanceInterface $override */ |
| 196 | if (!$this->isOverrideOfCurrentDisplay($override)) { |
| 197 | continue; |
| 198 | } |
| 199 | |
| 200 | if ($override->getProfile()->id() === $profile_id) { |
| 201 | continue; |
| 202 | } |
| 203 | $override->setProfile($profile_id); |
| 204 | $override->save(); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | parent::postSave($storage, $update); |
| 208 | parent::postSave($storage, $update); |
| 209 | } |