display_builder-1.0.x-dev/modules/display_builder_entity_view/src/Entity/DisplayBuilderEntityViewDisplay.php

modules/display_builder_entity_view/src/Entity/DisplayBuilderEntityViewDisplay.php
<?php

declare(strict_types=1);

namespace Drupal\display_builder_entity_view\Entity;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\Action\Attribute\ActionMethod;
use Drupal\Core\Entity\Entity\EntityViewDisplay as BaseEntityViewDisplay;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\display_builder_entity_view\Controller\DisplayBuilderEntityViewController;

/**
 * Provides an entity view display entity that has a display builder.
 */
class DisplayBuilderEntityViewDisplay extends BaseEntityViewDisplay {

  /**
   * The entity field manager.
   *
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $entityFieldManager;

  /**
   * The source plugin manager.
   *
   * @var \Drupal\ui_patterns\SourcePluginManager
   */
  protected $sourcePluginManager;

  /**
   * The state manager service.
   *
   * @var \Drupal\display_builder\StateManager\StateManagerInterface
   */
  protected $stateManager;

  /**
   * The component element builder service.
   *
   * @var \Drupal\ui_patterns\Element\ComponentElementBuilder
   */
  protected $componentElementBuilder;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $values, $entity_type) {
    // Set $entityFieldManager before calling the parent constructor because the
    // constructor will call init() which then calls setComponent() which needs
    // $entityFieldManager.
    $this->entityFieldManager = \Drupal::service('entity_field.manager');
    $this->sourcePluginManager = \Drupal::service('plugin.manager.ui_patterns_source');
    $this->stateManager = \Drupal::service('display_builder.state_manager');
    $this->componentElementBuilder = \Drupal::service('ui_patterns.component_element_builder');
    parent::__construct($values, $entity_type);
  }

  /**
   * Compatibility with layout_builder module.
   *
   * Sadly, we need to define this method
   * to be able to cohabit with layout_builder module.
   *
   * @return bool
   *   Always returns FALSE.
   */
  public function isLayoutBuilderEnabled(): bool {
    return FALSE;
  }

  /**
   * Compatibility with layout_builder module.
   *
   * Sadly, we need to define this method
   * to be able to cohabit with layout_builder module.
   *
   * @return bool
   *   Always returns FALSE.
   */
  public function isOverridable(): bool {
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function buildMultiple(array $entities) {
    $build_list = parent::buildMultiple($entities);

    // If no display builder enable stop here.
    if (!$this->isDisplayBuilderEnabled()) {
      return $build_list;
    }

    // Display Builder can not be enabled for the '_custom' view mode that is
    // used for on-the-fly rendering of fields in isolation from the entity.
    if ($this->isCustomMode()) {
      return $build_list;
    }

    foreach ($entities as $id => $entity) {
      $build_list[$id]['_display_builder'] = $this->buildSources($entity);

      // Remove all fields with configurable display
      // from the existing build.
      foreach (array_keys($build_list[$id]) as $name) {
        $field_definition = $this->getFieldDefinition($name);

        if ($field_definition && $field_definition->isDisplayConfigurable($this->displayContext)) {
          unset($build_list[$id][$name]);
        }
      }
    }

    return $build_list;
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    parent::calculateDependencies();
    $contexts = $this->stateManager->getContexts($this->getDisplayBuilderId());

    foreach ($this->getSources() as $source_data) {
      /** @var \Drupal\ui_patterns\SourceInterface $source */
      $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts);
      $this->addDependencies($source->calculateDependencies());
    }

    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function createCopy($mode): static {
    // Disable Display Builder and remove any sources copied from the original.
    return parent::createCopy($mode)
      ->setSources([])
      ->disableDisplayBuilder();
  }

  /**
   * {@inheritdoc}
   */
  #[ActionMethod(adminLabel: new TranslatableMarkup('Disable Display Builder'), pluralize: FALSE)]
  public function disableDisplayBuilder(): static {
    $this->setThirdPartySetting('display_builder', 'enabled', FALSE);

    return $this;
  }

  /**
   * {@inheritdoc}
   */
  #[ActionMethod(adminLabel: new TranslatableMarkup('Enable Display Builder'), pluralize: FALSE)]
  public function enableDisplayBuilder(): static {
    $this->setThirdPartySetting('display_builder', 'enabled', TRUE);

    return $this;
  }

  /**
   * Return sources.
   *
   * @return array
   *   The sources.
   */
  public function getSources(): array {
    return $this->getThirdPartySetting('display_builder', 'sources', []);
  }

  /**
   * {@inheritdoc}
   */
  public function isDisplayBuilderEnabled(): bool {
    // Display Builder must not be enabled for the '_custom' view mode that is
    // used for on-the-fly rendering of fields in isolation from the entity.
    if ($this->isCustomMode()) {
      return FALSE;
    }

    return (bool) $this->getThirdPartySetting('display_builder', 'enabled');
  }

  /**
   * {@inheritdoc}
   *
   * @todo Move this upstream in https://www.drupal.org/node/2939931.
   */
  public function label() {
    $bundle_info = \Drupal::service('entity_type.bundle.info')->getBundleInfo($this->getTargetEntityTypeId());
    $bundle_label = $bundle_info[$this->getTargetBundle()]['label'];
    $target_entity_type = $this->entityTypeManager()->getDefinition($this->getTargetEntityTypeId());

    return new TranslatableMarkup('@bundle @label', [
      '@bundle' => $bundle_label,
      '@label' => $target_entity_type->getPluralLabel(),
    ]);
  }

  /**
   * {@inheritdoc}
   */
  public function onDependencyRemoval(array $dependencies) {
    $changed = parent::onDependencyRemoval($dependencies);

    // Loop through all sources and determine if the removed dependencies are
    // used by their layout plugins.
    $contexts = $this->stateManager->getContexts($this->getDisplayBuilderId());

    foreach ($this->getSources() as $delta => $source_data) {
      /** @var \Drupal\ui_patterns\SourceInterface $source */
      $source = $this->sourcePluginManager->getSource('', [], $source_data, $contexts);
      $source_dependencies = $source->calculateDependencies();
      $source_removed_dependencies = $this->getPluginRemovedDependencies($source_dependencies, $dependencies);

      if ($source_removed_dependencies) {
        // @todo Allow the plugins to react to their dependency removal in
        //   https://www.drupal.org/project/drupal/issues/2579743.
        $this->removeSource($delta);
        $changed = TRUE;
      }
    }

    return $changed;
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage): void {
    parent::preSave($storage);

    $already_enabled = isset($this->original) ? $this->original->isDisplayBuilderEnabled() : FALSE;
    $set_enabled = $this->isDisplayBuilderEnabled();

    if ($already_enabled !== $set_enabled) {
      if ($set_enabled) {
        // Loop through all existing field-based components and add them as
        // pre-configured sources ? (like layout builder does).
      }
      else {
        // When being disabled, remove all existing source data.
        $this->removeAllSources();
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function removeAllSources(): static {
    $this->setSources([]);

    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function removeSource(mixed $delta): static {
    $sources = $this->getSources();
    unset($sources[$delta]);
    $this->setSources($sources);

    return $this;
  }

  /**
   * Builds the render array for the sources of a given entity.
   *
   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
   *   The entity.
   *
   * @return array
   *   The render array representing the sources of the entity.
   */
  protected function buildSources(FieldableEntityInterface $entity) {
    $contexts = $this->getContextsForEntity($entity);
    $label = new TranslatableMarkup('@entity being viewed', [
      '@entity' => $entity->getEntityType()->getSingularLabel(),
    ]);
    $contexts['display_builder.entity'] = EntityContext::fromEntity($entity, (string) $label);

    $cacheability = new CacheableMetadata();
    $fake_build = [];

    foreach ($this->getSources() as $source_data) {
      $fake_build = $this->componentElementBuilder->buildSource($fake_build, 'content', [], $source_data, $contexts);
    }
    $build = $fake_build['#slots']['content'] ?? [];
    $build['#cache'] = $fake_build['#cache'] ?? [];
    // The render array is built based on decisions made by SourceStorage
    // plugins and therefore it needs to depend on the accumulated
    // cacheability of those decisions.
    $cacheability->applyTo($build);

    return $build;
  }

  /**
   * Wraps the context repository service.
   *
   * @return \Drupal\Core\Plugin\Context\ContextRepositoryInterface
   *   The context repository service.
   */
  protected function contextRepository() {
    return \Drupal::service('context.repository');
  }

  /**
   * Gets the available contexts for a given entity.
   *
   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
   *   The entity.
   *
   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
   *   An array of context objects for a given entity.
   */
  protected function getContextsForEntity(FieldableEntityInterface $entity) {
    $available_context_ids = \array_keys($this->contextRepository()->getAvailableContexts());

    return [
      'view_mode' => new Context(ContextDefinition::create('string'), $this->getMode()),
      'entity' => EntityContext::fromEntity($entity),
      'display' => EntityContext::fromEntity($this),
    ] + $this->contextRepository()->getRuntimeContexts($available_context_ids);
  }

  /**
   * Get the display builder ID.
   *
   * @return string
   *   The display builder ID.
   */
  protected function getDisplayBuilderId(): string {
    $entity_type_id = $this->getTargetEntityTypeId();
    $bundle = $this->getTargetBundle();
    $view_mode_name = $this->getMode();

    return DisplayBuilderEntityViewController::getDisplayBuilderId($entity_type_id, $bundle, $view_mode_name);
  }

  /**
   * Indicates if this display is using the '_custom' view mode.
   *
   * @return bool
   *   TRUE if this display is using the '_custom' view mode, FALSE otherwise.
   */
  protected function isCustomMode() {
    return $this->getOriginalMode() === static::CUSTOM_MODE;
  }

  /**
   * {@inheritdoc}
   */
  protected function setSources(array $sources): static {
    // Third-party settings must be completely unset instead of stored as an
    // empty array.
    if (!$sources) {
      $this->unsetThirdPartySetting('display_builder', 'sources');
    }
    else {
      $this->setThirdPartySetting('display_builder', 'sources', \array_values($sources));
    }

    return $this;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc