annotate core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents a9cd425dd02b
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\layout_builder\Entity;
Chris@0 4
Chris@5 5 use Drupal\Component\Plugin\ConfigurableInterface;
Chris@5 6 use Drupal\Component\Plugin\DerivativeInspectionInterface;
Chris@5 7 use Drupal\Component\Plugin\PluginBase;
Chris@5 8 use Drupal\Core\Cache\CacheableMetadata;
Chris@0 9 use Drupal\Core\Entity\Entity\EntityViewDisplay as BaseEntityViewDisplay;
Chris@0 10 use Drupal\Core\Entity\EntityStorageInterface;
Chris@0 11 use Drupal\Core\Entity\FieldableEntityInterface;
Chris@5 12 use Drupal\Core\Plugin\Context\Context;
Chris@5 13 use Drupal\Core\Plugin\Context\ContextDefinition;
Chris@4 14 use Drupal\Core\Plugin\Context\EntityContext;
Chris@5 15 use Drupal\Core\Render\Element;
Chris@0 16 use Drupal\Core\StringTranslation\TranslatableMarkup;
Chris@0 17 use Drupal\field\Entity\FieldConfig;
Chris@0 18 use Drupal\field\Entity\FieldStorageConfig;
Chris@5 19 use Drupal\layout_builder\LayoutEntityHelperTrait;
Chris@4 20 use Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage;
Chris@5 21 use Drupal\layout_builder\QuickEditIntegration;
Chris@0 22 use Drupal\layout_builder\Section;
Chris@0 23 use Drupal\layout_builder\SectionComponent;
Chris@0 24 use Drupal\layout_builder\SectionStorage\SectionStorageTrait;
Chris@0 25
Chris@0 26 /**
Chris@0 27 * Provides an entity view display entity that has a layout.
Chris@0 28 */
Chris@0 29 class LayoutBuilderEntityViewDisplay extends BaseEntityViewDisplay implements LayoutEntityDisplayInterface {
Chris@0 30
Chris@0 31 use SectionStorageTrait;
Chris@5 32 use LayoutEntityHelperTrait;
Chris@0 33
Chris@0 34 /**
Chris@4 35 * The entity field manager.
Chris@4 36 *
Chris@4 37 * @var \Drupal\Core\Entity\EntityFieldManagerInterface
Chris@4 38 */
Chris@4 39 protected $entityFieldManager;
Chris@4 40
Chris@4 41 /**
Chris@4 42 * {@inheritdoc}
Chris@4 43 */
Chris@4 44 public function __construct(array $values, $entity_type) {
Chris@4 45 // Set $entityFieldManager before calling the parent constructor because the
Chris@4 46 // constructor will call init() which then calls setComponent() which needs
Chris@4 47 // $entityFieldManager.
Chris@4 48 $this->entityFieldManager = \Drupal::service('entity_field.manager');
Chris@4 49 parent::__construct($values, $entity_type);
Chris@4 50 }
Chris@4 51
Chris@4 52 /**
Chris@0 53 * {@inheritdoc}
Chris@0 54 */
Chris@0 55 public function isOverridable() {
Chris@5 56 return $this->isLayoutBuilderEnabled() && $this->getThirdPartySetting('layout_builder', 'allow_custom', FALSE);
Chris@0 57 }
Chris@0 58
Chris@0 59 /**
Chris@0 60 * {@inheritdoc}
Chris@0 61 */
Chris@0 62 public function setOverridable($overridable = TRUE) {
Chris@0 63 $this->setThirdPartySetting('layout_builder', 'allow_custom', $overridable);
Chris@5 64 // Enable Layout Builder if it's not already enabled and overriding.
Chris@5 65 if ($overridable && !$this->isLayoutBuilderEnabled()) {
Chris@5 66 $this->enableLayoutBuilder();
Chris@5 67 }
Chris@0 68 return $this;
Chris@0 69 }
Chris@0 70
Chris@0 71 /**
Chris@0 72 * {@inheritdoc}
Chris@0 73 */
Chris@4 74 public function isLayoutBuilderEnabled() {
Chris@5 75 // To prevent infinite recursion, Layout Builder must not be enabled for the
Chris@5 76 // '_custom' view mode that is used for on-the-fly rendering of fields in
Chris@5 77 // isolation from the entity.
Chris@5 78 if ($this->getOriginalMode() === static::CUSTOM_MODE) {
Chris@5 79 return FALSE;
Chris@5 80 }
Chris@4 81 return (bool) $this->getThirdPartySetting('layout_builder', 'enabled');
Chris@4 82 }
Chris@4 83
Chris@4 84 /**
Chris@4 85 * {@inheritdoc}
Chris@4 86 */
Chris@4 87 public function enableLayoutBuilder() {
Chris@4 88 $this->setThirdPartySetting('layout_builder', 'enabled', TRUE);
Chris@4 89 return $this;
Chris@4 90 }
Chris@4 91
Chris@4 92 /**
Chris@4 93 * {@inheritdoc}
Chris@4 94 */
Chris@4 95 public function disableLayoutBuilder() {
Chris@4 96 $this->setOverridable(FALSE);
Chris@4 97 $this->setThirdPartySetting('layout_builder', 'enabled', FALSE);
Chris@4 98 return $this;
Chris@4 99 }
Chris@4 100
Chris@4 101 /**
Chris@4 102 * {@inheritdoc}
Chris@4 103 */
Chris@0 104 public function getSections() {
Chris@0 105 return $this->getThirdPartySetting('layout_builder', 'sections', []);
Chris@0 106 }
Chris@0 107
Chris@0 108 /**
Chris@0 109 * {@inheritdoc}
Chris@0 110 */
Chris@0 111 protected function setSections(array $sections) {
Chris@5 112 // Third-party settings must be completely unset instead of stored as an
Chris@5 113 // empty array.
Chris@5 114 if (!$sections) {
Chris@5 115 $this->unsetThirdPartySetting('layout_builder', 'sections');
Chris@5 116 }
Chris@5 117 else {
Chris@5 118 $this->setThirdPartySetting('layout_builder', 'sections', array_values($sections));
Chris@5 119 }
Chris@0 120 return $this;
Chris@0 121 }
Chris@0 122
Chris@0 123 /**
Chris@0 124 * {@inheritdoc}
Chris@0 125 */
Chris@0 126 public function preSave(EntityStorageInterface $storage) {
Chris@0 127 parent::preSave($storage);
Chris@0 128
Chris@0 129 $original_value = isset($this->original) ? $this->original->isOverridable() : FALSE;
Chris@0 130 $new_value = $this->isOverridable();
Chris@0 131 if ($original_value !== $new_value) {
Chris@0 132 $entity_type_id = $this->getTargetEntityTypeId();
Chris@0 133 $bundle = $this->getTargetBundle();
Chris@0 134
Chris@0 135 if ($new_value) {
Chris@4 136 $this->addSectionField($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME);
Chris@0 137 }
Chris@4 138 else {
Chris@4 139 $this->removeSectionField($entity_type_id, $bundle, OverridesSectionStorage::FIELD_NAME);
Chris@0 140 }
Chris@0 141 }
Chris@4 142
Chris@4 143 $already_enabled = isset($this->original) ? $this->original->isLayoutBuilderEnabled() : FALSE;
Chris@4 144 $set_enabled = $this->isLayoutBuilderEnabled();
Chris@4 145 if ($already_enabled !== $set_enabled) {
Chris@4 146 if ($set_enabled) {
Chris@4 147 // Loop through all existing field-based components and add them as
Chris@4 148 // section-based components.
Chris@4 149 $components = $this->getComponents();
Chris@4 150 // Sort the components by weight.
Chris@4 151 uasort($components, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
Chris@4 152 foreach ($components as $name => $component) {
Chris@4 153 $this->setComponent($name, $component);
Chris@4 154 }
Chris@4 155 }
Chris@4 156 else {
Chris@4 157 // When being disabled, remove all existing section data.
Chris@5 158 $this->removeAllSections();
Chris@4 159 }
Chris@4 160 }
Chris@4 161 }
Chris@4 162
Chris@4 163 /**
Chris@4 164 * Removes a layout section field if it is no longer needed.
Chris@4 165 *
Chris@4 166 * Because the field is shared across all view modes, the field will only be
Chris@4 167 * removed if no other view modes are using it.
Chris@4 168 *
Chris@4 169 * @param string $entity_type_id
Chris@4 170 * The entity type ID.
Chris@4 171 * @param string $bundle
Chris@4 172 * The bundle.
Chris@4 173 * @param string $field_name
Chris@4 174 * The name for the layout section field.
Chris@4 175 */
Chris@4 176 protected function removeSectionField($entity_type_id, $bundle, $field_name) {
Chris@4 177 $query = $this->entityTypeManager()->getStorage($this->getEntityTypeId())->getQuery()
Chris@4 178 ->condition('targetEntityType', $this->getTargetEntityTypeId())
Chris@4 179 ->condition('bundle', $this->getTargetBundle())
Chris@4 180 ->condition('mode', $this->getMode(), '<>')
Chris@4 181 ->condition('third_party_settings.layout_builder.allow_custom', TRUE);
Chris@4 182 $enabled = (bool) $query->count()->execute();
Chris@4 183 if (!$enabled && $field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name)) {
Chris@4 184 $field->delete();
Chris@4 185 }
Chris@0 186 }
Chris@0 187
Chris@0 188 /**
Chris@0 189 * Adds a layout section field to a given bundle.
Chris@0 190 *
Chris@0 191 * @param string $entity_type_id
Chris@0 192 * The entity type ID.
Chris@0 193 * @param string $bundle
Chris@0 194 * The bundle.
Chris@0 195 * @param string $field_name
Chris@0 196 * The name for the layout section field.
Chris@0 197 */
Chris@0 198 protected function addSectionField($entity_type_id, $bundle, $field_name) {
Chris@0 199 $field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name);
Chris@0 200 if (!$field) {
Chris@0 201 $field_storage = FieldStorageConfig::loadByName($entity_type_id, $field_name);
Chris@0 202 if (!$field_storage) {
Chris@0 203 $field_storage = FieldStorageConfig::create([
Chris@0 204 'entity_type' => $entity_type_id,
Chris@0 205 'field_name' => $field_name,
Chris@0 206 'type' => 'layout_section',
Chris@0 207 'locked' => TRUE,
Chris@0 208 ]);
Chris@5 209 $field_storage->setTranslatable(FALSE);
Chris@0 210 $field_storage->save();
Chris@0 211 }
Chris@0 212
Chris@0 213 $field = FieldConfig::create([
Chris@0 214 'field_storage' => $field_storage,
Chris@0 215 'bundle' => $bundle,
Chris@0 216 'label' => t('Layout'),
Chris@0 217 ]);
Chris@5 218 $field->setTranslatable(FALSE);
Chris@0 219 $field->save();
Chris@0 220 }
Chris@0 221 }
Chris@0 222
Chris@0 223 /**
Chris@0 224 * {@inheritdoc}
Chris@0 225 */
Chris@4 226 public function createCopy($mode) {
Chris@4 227 // Disable Layout Builder and remove any sections copied from the original.
Chris@4 228 return parent::createCopy($mode)
Chris@4 229 ->setSections([])
Chris@4 230 ->disableLayoutBuilder();
Chris@4 231 }
Chris@4 232
Chris@4 233 /**
Chris@4 234 * {@inheritdoc}
Chris@4 235 */
Chris@0 236 protected function getDefaultRegion() {
Chris@0 237 if ($this->hasSection(0)) {
Chris@0 238 return $this->getSection(0)->getDefaultRegion();
Chris@0 239 }
Chris@0 240
Chris@0 241 return parent::getDefaultRegion();
Chris@0 242 }
Chris@0 243
Chris@0 244 /**
Chris@0 245 * Wraps the context repository service.
Chris@0 246 *
Chris@0 247 * @return \Drupal\Core\Plugin\Context\ContextRepositoryInterface
Chris@0 248 * The context repository service.
Chris@0 249 */
Chris@0 250 protected function contextRepository() {
Chris@0 251 return \Drupal::service('context.repository');
Chris@0 252 }
Chris@0 253
Chris@0 254 /**
Chris@0 255 * {@inheritdoc}
Chris@0 256 */
Chris@0 257 public function buildMultiple(array $entities) {
Chris@0 258 $build_list = parent::buildMultiple($entities);
Chris@0 259
Chris@0 260 foreach ($entities as $id => $entity) {
Chris@5 261 $build_list[$id]['_layout_builder'] = $this->buildSections($entity);
Chris@5 262
Chris@5 263 // If there are any sections, remove all fields with configurable display
Chris@5 264 // from the existing build. These fields are replicated within sections as
Chris@5 265 // field blocks by ::setComponent().
Chris@5 266 if (!Element::isEmpty($build_list[$id]['_layout_builder'])) {
Chris@0 267 foreach ($build_list[$id] as $name => $build_part) {
Chris@0 268 $field_definition = $this->getFieldDefinition($name);
Chris@0 269 if ($field_definition && $field_definition->isDisplayConfigurable($this->displayContext)) {
Chris@0 270 unset($build_list[$id][$name]);
Chris@0 271 }
Chris@0 272 }
Chris@0 273 }
Chris@0 274 }
Chris@0 275
Chris@0 276 return $build_list;
Chris@0 277 }
Chris@0 278
Chris@0 279 /**
Chris@5 280 * Builds the render array for the sections of a given entity.
Chris@5 281 *
Chris@5 282 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
Chris@5 283 * The entity.
Chris@5 284 *
Chris@5 285 * @return array
Chris@5 286 * The render array representing the sections of the entity.
Chris@5 287 */
Chris@5 288 protected function buildSections(FieldableEntityInterface $entity) {
Chris@5 289 $contexts = $this->getContextsForEntity($entity);
Chris@5 290 // @todo Remove in https://www.drupal.org/project/drupal/issues/3018782.
Chris@5 291 $label = new TranslatableMarkup('@entity being viewed', [
Chris@5 292 '@entity' => $entity->getEntityType()->getSingularLabel(),
Chris@5 293 ]);
Chris@5 294 $contexts['layout_builder.entity'] = EntityContext::fromEntity($entity, $label);
Chris@5 295
Chris@5 296 $cacheability = new CacheableMetadata();
Chris@5 297 $storage = $this->sectionStorageManager()->findByContext($contexts, $cacheability);
Chris@5 298
Chris@5 299 $build = [];
Chris@5 300 if ($storage) {
Chris@5 301 foreach ($storage->getSections() as $delta => $section) {
Chris@5 302 $build[$delta] = $section->toRenderArray($contexts);
Chris@5 303 }
Chris@5 304 }
Chris@5 305 // The render array is built based on decisions made by @SectionStorage
Chris@5 306 // plugins and therefore it needs to depend on the accumulated
Chris@5 307 // cacheability of those decisions.
Chris@5 308 $cacheability->applyTo($build);
Chris@5 309 return $build;
Chris@5 310 }
Chris@5 311
Chris@5 312 /**
Chris@5 313 * Gets the available contexts for a given entity.
Chris@5 314 *
Chris@5 315 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
Chris@5 316 * The entity.
Chris@5 317 *
Chris@5 318 * @return \Drupal\Core\Plugin\Context\ContextInterface[]
Chris@5 319 * An array of context objects for a given entity.
Chris@5 320 */
Chris@5 321 protected function getContextsForEntity(FieldableEntityInterface $entity) {
Chris@5 322 return [
Chris@5 323 'view_mode' => new Context(ContextDefinition::create('string'), $this->getMode()),
Chris@5 324 'entity' => EntityContext::fromEntity($entity),
Chris@5 325 'display' => EntityContext::fromEntity($this),
Chris@5 326 ] + $this->contextRepository()->getAvailableContexts();
Chris@5 327 }
Chris@5 328
Chris@5 329 /**
Chris@0 330 * Gets the runtime sections for a given entity.
Chris@0 331 *
Chris@0 332 * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
Chris@0 333 * The entity.
Chris@0 334 *
Chris@0 335 * @return \Drupal\layout_builder\Section[]
Chris@0 336 * The sections.
Chris@5 337 *
Chris@5 338 * @deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0.
Chris@5 339 * \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::findByContext()
Chris@5 340 * should be used instead. See https://www.drupal.org/node/3022574.
Chris@0 341 */
Chris@0 342 protected function getRuntimeSections(FieldableEntityInterface $entity) {
Chris@5 343 @trigger_error('\Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay::getRuntimeSections() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface::findByContext() should be used instead. See https://www.drupal.org/node/3022574.', E_USER_DEPRECATED);
Chris@5 344 // For backwards compatibility, mimic the functionality of ::buildSections()
Chris@5 345 // by constructing a cacheable metadata object and retrieving the
Chris@5 346 // entity-based contexts.
Chris@5 347 $cacheability = new CacheableMetadata();
Chris@5 348 $contexts = $this->getContextsForEntity($entity);
Chris@5 349 $storage = $this->sectionStorageManager()->findByContext($contexts, $cacheability);
Chris@5 350 return $storage ? $storage->getSections() : [];
Chris@0 351 }
Chris@0 352
Chris@0 353 /**
Chris@0 354 * {@inheritdoc}
Chris@0 355 *
Chris@0 356 * @todo Move this upstream in https://www.drupal.org/node/2939931.
Chris@0 357 */
Chris@0 358 public function label() {
Chris@0 359 $bundle_info = \Drupal::service('entity_type.bundle.info')->getBundleInfo($this->getTargetEntityTypeId());
Chris@0 360 $bundle_label = $bundle_info[$this->getTargetBundle()]['label'];
Chris@0 361 $target_entity_type = $this->entityTypeManager()->getDefinition($this->getTargetEntityTypeId());
Chris@0 362 return new TranslatableMarkup('@bundle @label', ['@bundle' => $bundle_label, '@label' => $target_entity_type->getPluralLabel()]);
Chris@0 363 }
Chris@0 364
Chris@0 365 /**
Chris@0 366 * {@inheritdoc}
Chris@0 367 */
Chris@0 368 public function calculateDependencies() {
Chris@0 369 parent::calculateDependencies();
Chris@0 370
Chris@0 371 foreach ($this->getSections() as $delta => $section) {
Chris@0 372 $this->calculatePluginDependencies($section->getLayout());
Chris@0 373 foreach ($section->getComponents() as $uuid => $component) {
Chris@0 374 $this->calculatePluginDependencies($component->getPlugin());
Chris@0 375 }
Chris@0 376 }
Chris@0 377
Chris@0 378 return $this;
Chris@0 379 }
Chris@0 380
Chris@0 381 /**
Chris@0 382 * {@inheritdoc}
Chris@0 383 */
Chris@0 384 public function onDependencyRemoval(array $dependencies) {
Chris@0 385 $changed = parent::onDependencyRemoval($dependencies);
Chris@0 386
Chris@0 387 // Loop through all sections and determine if the removed dependencies are
Chris@0 388 // used by their layout plugins.
Chris@0 389 foreach ($this->getSections() as $delta => $section) {
Chris@0 390 $layout_dependencies = $this->getPluginDependencies($section->getLayout());
Chris@0 391 $layout_removed_dependencies = $this->getPluginRemovedDependencies($layout_dependencies, $dependencies);
Chris@0 392 if ($layout_removed_dependencies) {
Chris@0 393 // @todo Allow the plugins to react to their dependency removal in
Chris@0 394 // https://www.drupal.org/project/drupal/issues/2579743.
Chris@0 395 $this->removeSection($delta);
Chris@0 396 $changed = TRUE;
Chris@0 397 }
Chris@0 398 // If the section is not removed, loop through all components.
Chris@0 399 else {
Chris@0 400 foreach ($section->getComponents() as $uuid => $component) {
Chris@0 401 $plugin_dependencies = $this->getPluginDependencies($component->getPlugin());
Chris@0 402 $component_removed_dependencies = $this->getPluginRemovedDependencies($plugin_dependencies, $dependencies);
Chris@0 403 if ($component_removed_dependencies) {
Chris@0 404 // @todo Allow the plugins to react to their dependency removal in
Chris@0 405 // https://www.drupal.org/project/drupal/issues/2579743.
Chris@0 406 $section->removeComponent($uuid);
Chris@0 407 $changed = TRUE;
Chris@0 408 }
Chris@0 409 }
Chris@0 410 }
Chris@0 411 }
Chris@0 412 return $changed;
Chris@0 413 }
Chris@0 414
Chris@0 415 /**
Chris@0 416 * {@inheritdoc}
Chris@0 417 */
Chris@0 418 public function setComponent($name, array $options = []) {
Chris@0 419 parent::setComponent($name, $options);
Chris@0 420
Chris@4 421 // Only continue if Layout Builder is enabled.
Chris@4 422 if (!$this->isLayoutBuilderEnabled()) {
Chris@0 423 return $this;
Chris@0 424 }
Chris@0 425
Chris@0 426 // Retrieve the updated options after the parent:: call.
Chris@0 427 $options = $this->content[$name];
Chris@0 428 // Provide backwards compatibility by converting to a section component.
Chris@0 429 $field_definition = $this->getFieldDefinition($name);
Chris@4 430 $extra_fields = $this->entityFieldManager->getExtraFields($this->getTargetEntityTypeId(), $this->getTargetBundle());
Chris@4 431 $is_view_configurable_non_extra_field = $field_definition && $field_definition->isDisplayConfigurable('view') && isset($options['type']);
Chris@4 432 if ($is_view_configurable_non_extra_field || isset($extra_fields['display'][$name])) {
Chris@4 433 $configuration = [
Chris@4 434 'label_display' => '0',
Chris@4 435 'context_mapping' => ['entity' => 'layout_builder.entity'],
Chris@4 436 ];
Chris@4 437 if ($is_view_configurable_non_extra_field) {
Chris@4 438 $configuration['id'] = 'field_block:' . $this->getTargetEntityTypeId() . ':' . $this->getTargetBundle() . ':' . $name;
Chris@4 439 $keys = array_flip(['type', 'label', 'settings', 'third_party_settings']);
Chris@4 440 $configuration['formatter'] = array_intersect_key($options, $keys);
Chris@4 441 }
Chris@4 442 else {
Chris@4 443 $configuration['id'] = 'extra_field_block:' . $this->getTargetEntityTypeId() . ':' . $this->getTargetBundle() . ':' . $name;
Chris@4 444 }
Chris@0 445
Chris@0 446 $section = $this->getDefaultSection();
Chris@0 447 $region = isset($options['region']) ? $options['region'] : $section->getDefaultRegion();
Chris@0 448 $new_component = (new SectionComponent(\Drupal::service('uuid')->generate(), $region, $configuration));
Chris@0 449 $section->appendComponent($new_component);
Chris@0 450 }
Chris@0 451 return $this;
Chris@0 452 }
Chris@0 453
Chris@0 454 /**
Chris@0 455 * Gets a default section.
Chris@0 456 *
Chris@0 457 * @return \Drupal\layout_builder\Section
Chris@0 458 * The default section.
Chris@0 459 */
Chris@0 460 protected function getDefaultSection() {
Chris@0 461 // If no section exists, append a new one.
Chris@0 462 if (!$this->hasSection(0)) {
Chris@0 463 $this->appendSection(new Section('layout_onecol'));
Chris@0 464 }
Chris@0 465
Chris@0 466 // Return the first section.
Chris@0 467 return $this->getSection(0);
Chris@0 468 }
Chris@0 469
Chris@5 470 /**
Chris@5 471 * Gets the section storage manager.
Chris@5 472 *
Chris@5 473 * @return \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface
Chris@5 474 * The section storage manager.
Chris@5 475 */
Chris@5 476 private function sectionStorageManager() {
Chris@5 477 return \Drupal::service('plugin.manager.layout_builder.section_storage');
Chris@5 478 }
Chris@5 479
Chris@5 480 /**
Chris@5 481 * {@inheritdoc}
Chris@5 482 */
Chris@5 483 public function getComponent($name) {
Chris@5 484 if ($this->isLayoutBuilderEnabled() && $section_component = $this->getQuickEditSectionComponent() ?: $this->getSectionComponentForFieldName($name)) {
Chris@5 485 $plugin = $section_component->getPlugin();
Chris@5 486 if ($plugin instanceof ConfigurableInterface) {
Chris@5 487 $configuration = $plugin->getConfiguration();
Chris@5 488 if (isset($configuration['formatter'])) {
Chris@5 489 return $configuration['formatter'];
Chris@5 490 }
Chris@5 491 }
Chris@5 492 }
Chris@5 493 return parent::getComponent($name);
Chris@5 494 }
Chris@5 495
Chris@5 496 /**
Chris@5 497 * Returns the Quick Edit formatter settings.
Chris@5 498 *
Chris@5 499 * @return \Drupal\layout_builder\SectionComponent|null
Chris@5 500 * The section component if it is available.
Chris@5 501 *
Chris@5 502 * @see \Drupal\layout_builder\QuickEditIntegration::entityViewAlter()
Chris@5 503 * @see \Drupal\quickedit\MetadataGenerator::generateFieldMetadata()
Chris@5 504 */
Chris@5 505 private function getQuickEditSectionComponent() {
Chris@5 506 // To determine the Quick Edit view_mode ID we need an originalMode set.
Chris@5 507 if ($original_mode = $this->getOriginalMode()) {
Chris@5 508 $parts = explode('-', $original_mode);
Chris@5 509 // The Quick Edit view mode ID is created by
Chris@5 510 // \Drupal\layout_builder\QuickEditIntegration::entityViewAlter()
Chris@5 511 // concatenating together the information we need to retrieve the Layout
Chris@5 512 // Builder component. It follows the structure prescribed by the
Chris@5 513 // documentation of hook_quickedit_render_field().
Chris@5 514 if (count($parts) === 6 && $parts[0] === 'layout_builder') {
Chris@5 515 list(, $delta, $component_uuid, $entity_id) = QuickEditIntegration::deconstructViewModeId($original_mode);
Chris@5 516 $entity = $this->entityTypeManager()->getStorage($this->getTargetEntityTypeId())->load($entity_id);
Chris@5 517 $sections = $this->getEntitySections($entity);
Chris@5 518 if (isset($sections[$delta])) {
Chris@5 519 $component = $sections[$delta]->getComponent($component_uuid);
Chris@5 520 $plugin = $component->getPlugin();
Chris@5 521 // We only care about FieldBlock because these are only components
Chris@5 522 // that provide Quick Edit integration: Quick Edit enables in-place
Chris@5 523 // editing of fields of entities, not of anything else.
Chris@5 524 if ($plugin instanceof DerivativeInspectionInterface && $plugin->getBaseId() === 'field_block') {
Chris@5 525 return $component;
Chris@5 526 }
Chris@5 527 }
Chris@5 528 }
Chris@5 529 }
Chris@5 530 return NULL;
Chris@5 531 }
Chris@5 532
Chris@5 533 /**
Chris@5 534 * Gets the component for a given field name if any.
Chris@5 535 *
Chris@5 536 * @param string $field_name
Chris@5 537 * The field name.
Chris@5 538 *
Chris@5 539 * @return \Drupal\layout_builder\SectionComponent|null
Chris@5 540 * The section component if it is available.
Chris@5 541 */
Chris@5 542 private function getSectionComponentForFieldName($field_name) {
Chris@5 543 // Loop through every component until the first match is found.
Chris@5 544 foreach ($this->getSections() as $section) {
Chris@5 545 foreach ($section->getComponents() as $component) {
Chris@5 546 $plugin = $component->getPlugin();
Chris@5 547 if ($plugin instanceof DerivativeInspectionInterface && $plugin->getBaseId() === 'field_block') {
Chris@5 548 // FieldBlock derivative IDs are in the format
Chris@5 549 // [entity_type]:[bundle]:[field].
Chris@5 550 list(, , $field_block_field_name) = explode(PluginBase::DERIVATIVE_SEPARATOR, $plugin->getDerivativeId());
Chris@5 551 if ($field_block_field_name === $field_name) {
Chris@5 552 return $component;
Chris@5 553 }
Chris@5 554 }
Chris@5 555 }
Chris@5 556 }
Chris@5 557 return NULL;
Chris@5 558 }
Chris@5 559
Chris@0 560 }