annotate core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php @ 19:fa3358dc1485 tip

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