layout_paragraphs-1.0.x-dev/src/LayoutParagraphsLayout.php

src/LayoutParagraphsLayout.php
<?php

namespace Drupal\layout_paragraphs;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
use Drupal\paragraphs\ParagraphInterface;

/**
 * Provides a domain object for a complete Layout Paragraphs Layout.
 *
 * A Layout Paragraphs Layout represents a collection of
 * Layout Paragraphs Sections and Layout Paragraphs Components
 * associated with a paragraphs reference field.
 * This class provides public methods for manipulating a layout -
 * i.e. adding, removing, and reordering paragraph layout sections
 * and paragraph layout components.
 *
 * See also:
 * - Drupal\layout_paragraphs\LayoutParagraphsComponent
 * - Drupal\layout_paragraphs\LayoutParagraphsSection
 */
class LayoutParagraphsLayout implements ThirdPartySettingsInterface {

  use DependencySerializationTrait;

  /**
   * The paragraph reference field the layout is attached to.
   *
   * @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface
   */
  protected $paragraphsReferenceField;

  /**
   * Third party settings.
   *
   * An array of key/value pairs keyed by provider.
   *
   * @var array[]
   */
  protected $thirdPartySettings = [];


  /**
   * Settings.
   *
   * An array of key/value pairs.
   *
   * @var array[]
   */
  protected $settings;

  /**
   * The layout ID.
   *
   * @var string
   */
  protected $id;

  /**
   * The layout's parent entity.
   *
   * @var \Drupal\Core\Entity\EntityInterface
   */
  protected $entity;

  /**
   * Class constructor.
   *
   * @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $paragraphs_reference_field
   *   The paragraph reference field this layout is attached to.
   * @param array[] $settings
   *   An array of settings.
   */
  public function __construct(
    EntityReferenceFieldItemListInterface $paragraphs_reference_field,
    array $settings = [],
  ) {
    $this->paragraphsReferenceField = $paragraphs_reference_field;
    $this->settings = $settings;
  }

  /**
   * Returns a unique id for this layout.
   *
   * @return string
   *   A unique id.
   */
  public function id() {
    if (empty($this->id)) {
      $this->id = bin2hex(random_bytes(16));
    }
    return $this->id;
  }

  /**
   * Returns the layout's parent entity with updated paragraphs reference field.
   *
   * @return \Drupal\Core\Entity\EntityInterface
   *   The entity.
   */
  public function getEntity() {
    $entity = $this->paragraphsReferenceField->getEntity();
    return $entity;
  }

  /**
   * Set the entity that this layout is attached to.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity to set.
   *
   * @return $this
   */
  public function setEntity(EntityInterface $entity) {
    $this->entity = $entity;
    return $this;
  }

  /**
   * Sets the settings array.
   *
   * @param array[] $settings
   *   An associative array of settings.
   *
   * @return $this
   */
  public function setSettings(array $settings) {
    $this->settings = $settings;
    return $this;
  }

  /**
   * Returns the settings array.
   */
  public function getSettings() {
    return $this->settings;
  }

  /**
   * Returns a single setting from the settings array.
   *
   * @param string $key
   *   The key of the setting to return.
   * @param mixed $default
   *   The default value to return if the setting is empty.
   */
  public function getSetting(string $key, $default = NULL) {
    return $this->settings[$key] ?? $default;
  }

  /**
   * Returns the reference field that this layout is attached to.
   *
   * @return \Drupal\Core\Field\EntityReferenceFieldItemListInterface
   *   The field item list.
   */
  public function &getParagraphsReferenceField() {
    return $this->paragraphsReferenceField;
  }

  /**
   * Set the field item list.
   *
   * @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $paragraphs_reference_field
   *   The field item list to set.
   *
   * @return $this
   */
  public function setParagraphsReferenceField(EntityReferenceFieldItemListInterface $paragraphs_reference_field) {
    foreach ($paragraphs_reference_field as $key => $field_item) {
      if ($field_item->entity) {
        $paragraphs_reference_field[$key]->entity->_referringItem = $field_item;
        $paragraphs_reference_field[$key]->entity->_layoutParagraphsLayout = $this;
      }
    }
    $this->paragraphsReferenceField = $paragraphs_reference_field;
    return $this;
  }

  /**
   * Returns the field name.
   *
   * @return string
   *   The field name.
   */
  public function getFieldName() {
    /** @var \Drupal\field\Entity\FieldConfig $definition **/
    $definition = $this->paragraphsReferenceField->getFieldDefinition();
    $field_name = $definition->getName();
    return $field_name;
  }

  /**
   * Wraps the paragraph in the component class.
   *
   * @param \Drupal\paragraphs\ParagraphInterface $paragraph
   *   The paragraph entity.
   *
   * @return LayoutParagraphsComponent|LayoutParagraphsSection
   *   The component.
   */
  public function getComponent(ParagraphInterface $paragraph) {
    return new LayoutParagraphsComponent($paragraph);
  }

  /**
   * Returns the component with matching uuid.
   *
   * @param string $uuid
   *   The uuid to search for.
   *
   * @return LayoutParagraphsComponent|null
   *   The component.
   */
  public function getComponentByUuid($uuid) {
    foreach ($this->getEntities() as $entity) {
      if ($entity->uuid() == $uuid) {
        return $this->getComponent($entity);
      }
    }
  }

  /**
   * Returns a Layout Paragraphs Layout Section for the given paragraph.
   *
   * If the provided paragraph is not a layout section, returns false.
   *
   * @param \Drupal\paragraphs\ParagraphInterface $paragraph
   *   The paragraph.
   *
   * @return \Drupal\layout_paragraphs\LayoutParagraphsSection|false
   *   The layout section or false.
   */
  public function getLayoutSection(ParagraphInterface $paragraph) {
    if (!LayoutParagraphsSection::isLayoutComponent($paragraph)) {
      return FALSE;
    }
    $uuid = $paragraph->uuid();
    $components = array_filter(
      $this->getComponents(),
      function ($component) use ($uuid) {
        return $component->getParentUuid() == $uuid;
      }
    );
    return new LayoutParagraphsSection($paragraph, $components);
  }

  /**
   * Returns a list of root level components for this collection.
   *
   * @return array
   *   An array of root level layout paragraph components.
   */
  public function getRootComponents() {
    return array_filter($this->getComponents(), function ($component) {
      return $component->isRoot();
    });
  }

  /**
   * Returns a list of all components for this collection.
   *
   * @return array
   *   An array of layout paragraph components.
   */
  public function getComponents() {
    return array_map(function ($paragraph) {
      return $this->getComponent($paragraph);
    }, $this->getEntities());
  }

  /**
   * Returns a list of all paragraph entities associated with this collection.
   *
   * @return \Drupal\paragraphs\ParagraphInterface[]
   *   An array of paragraph entities.
   */
  public function getEntities() {
    $items = [];
    foreach ($this->paragraphsReferenceField as $field_item) {
      if ($field_item->entity) {
        $items[] = $field_item->entity;
      }
    }
    return $items;
  }

  /**
   * Determines whether the reference field contains any non-empty items.
   *
   * @return bool
   *   TRUE if the list is empty, FALSE otherwise.
   */
  public function isEmpty() {
    return $this->paragraphsReferenceField->isEmpty();
  }

  /**
   * Sets a layout component.
   *
   * If a component is found with a matching paragraph,
   * the matching component's paragraph is overwritten with the
   * incoming paragraph. Otherwise the paragraph is appended
   * to the field item list.
   *
   * @param \Drupal\paragraphs\ParagraphInterface $paragraph
   *   The paragraph to set.
   *
   * @return $this
   */
  public function setComponent(ParagraphInterface $paragraph) {
    $delta = $this->getComponentDeltaByUuid($paragraph->uuid());
    if ($delta > -1) {
      $this->paragraphsReferenceField[$delta]->entity = $paragraph;
    }
    else {
      $this->paragraphsReferenceField[] = $paragraph;
    }
    return $this;
  }

  /**
   * Reorder components.
   *
   * Accepts an associative of component uuids, parent uuids, and regions.
   *
   * @param array $ordered_items
   *   The nested array with the new order for items.
   *
   * @return $this
   */
  public function reorderComponents(array $ordered_items) {
    $reordered_items = [];
    foreach ($ordered_items as $ordered_item) {
      if ($component = $this->getComponentByUuid($ordered_item['uuid'])) {
        $component->setSettings([
          'parent_uuid' => $ordered_item['parentUuid'],
          'region' => $ordered_item['region'],
        ]);
        $reordered_items[] = [
          'entity' => $component->getEntity(),
        ];
      }
    }
    $this->paragraphsReferenceField->setValue($reordered_items);
    return $this;
  }

  /**
   * Duplicates a component.
   *
   * @param string $source_uuid
   *   The source uuid of the component to duplicate.
   * @param string $target_section_uuid
   *   The uuid of the target section component.
   *   If null, the clone will be inserted in the same section, after
   *   the source. If set, the clone will be inserted in the target section,
   *   appended in the same region as the source.
   *
   * @return \Drupal\layout_paragraphs\LayoutParagraphsComponent
   *   The duplicated component.
   */
  public function duplicateComponent(string $source_uuid, ?string $target_section_uuid = NULL) {
    $source_component = $this->getComponentByUuid($source_uuid);
    $cloned_paragraph = $source_component->getEntity()->createDuplicate();
    if ($target_section_uuid) {
      $this->insertIntoRegion(
        $target_section_uuid,
        $source_component->getRegion(),
        $cloned_paragraph,
      );
    }
    else {
      $this->insertAfterComponent($source_uuid, $cloned_paragraph);
    }
    if ($source_component->isLayout()) {
      /** @var \Drupal\layout_paragraphs\LayoutParagraphsSection $section */
      $section = $this->getLayoutSection($source_component->getEntity());
      foreach ($section->getComponents() as $component) {
        $this->duplicateComponent($component->getEntity()->uuid(), $cloned_paragraph->uuid());
      }
    }
    return $this->getComponent($cloned_paragraph);
  }

  /**
   * Insert a paragraph component before an existing component.
   *
   * @param string $parent_uuid
   *   The parent component's uuid.
   * @param string $region
   *   The region.
   * @param \Drupal\paragraphs\ParagraphInterface $paragraph
   *   The paragraph component to add.
   *
   * @return $this
   */
  public function insertIntoRegion(string $parent_uuid, string $region, ParagraphInterface $paragraph) {
    // Create a layout component for the new paragraph.
    $component = $this->getComponent($paragraph);
    // Make sure the parent component exists.
    if ($this->getComponentByUuid($parent_uuid)) {
      // Set the parent and region.
      $component->setSettings([
        'parent_uuid' => $parent_uuid,
        'region' => $region,
      ]);
      // Get the paragraph entity from the component.
      $new_paragraph = $component->getEntity();
      $new_paragraph->setParentEntity($this->getEntity(), $this->getFieldName());
      // Splice the new paragraph into the field item list.
      $item_list = $this->getParagraphsReferenceField();
      $list = $item_list->getValue();
      array_unshift($list, ['entity' => $new_paragraph]);
      $item_list->setValue($list);
      $this->setParagraphsReferenceField($item_list);
    }
    else {
      // @todo Throw exception.
    }
    return $this;
  }

  /**
   * Insert a paragraph component before an existing component.
   *
   * @param string $sibling_uuid
   *   The existing sibling paragraph component's uuid.
   * @param \Drupal\paragraphs\ParagraphInterface $paragraph
   *   The paragraph component to add.
   *
   * @return $this
   */
  public function insertBeforeComponent(string $sibling_uuid, ParagraphInterface $paragraph) {
    return $this->insertSiblingComponent($sibling_uuid, $paragraph);
  }

  /**
   * Insert a paragraph component after an existing component.
   *
   * @param string $sibling_uuid
   *   The existing sibling paragraph component's uuid.
   * @param \Drupal\paragraphs\ParagraphInterface $paragraph
   *   The paragraph component to add.
   *
   * @return $this
   */
  public function insertAfterComponent(string $sibling_uuid, ParagraphInterface $paragraph) {
    return $this->insertSiblingComponent($sibling_uuid, $paragraph, 1);
  }

  /**
   * Insert an new item adjacent to $sibling.
   *
   * @param string $sibling_uuid
   *   The existing sibling paragraph component's uuid.
   * @param \Drupal\paragraphs\ParagraphInterface $new_paragraph
   *   The paragraph component to add.
   * @param int $delta_offset
   *   Where to add the new item in relation to sibling.
   *
   * @return $this
   */
  protected function insertSiblingComponent(
    string $sibling_uuid,
    ParagraphInterface $new_paragraph,
    int $delta_offset = 0,
  ) {

    // Create a layout component for the new paragraph.
    $new_component = $this->getComponent($new_paragraph);
    // Find the existing sibling component, and copy the layout settings
    // into the new component to be inserted.
    if ($existing_component = $this->getComponentByUuid($sibling_uuid)) {
      // Copy layout settings into the new component.
      $sibling_settings = $existing_component->getSettings();
      $new_component_settings = [
        'parent_uuid' => $sibling_settings['parent_uuid'] ?? NULL,
        'region' => $sibling_settings['region'] ?? NULL,
      ];
      $new_component->setSettings($new_component_settings);
      // Get the paragraph entity from the component.
      $new_paragraph = $new_component->getEntity();
      $new_paragraph->setParentEntity($this->getEntity(), $this->getFieldName());
      // Splice the new paragraph into the field item list.
      $item_list = $this->getParagraphsReferenceField();
      $list = $item_list->getValue();
      $delta = $this->getComponentDeltaByUuid($sibling_uuid);
      $delta += $delta_offset;
      array_splice($list, $delta, 0, ['entity' => $new_paragraph]);
      $item_list->setValue($list);
      $this->setParagraphsReferenceField($item_list);
    }
    else {
      // @todo Throw exception.
    }
    return $this;
  }

  /**
   * Append a new component.
   *
   * @param \Drupal\paragraphs\ParagraphInterface $new_paragraph
   *   The paragraph component to append.
   *
   * @return $this
   */
  public function appendComponent(ParagraphInterface $new_paragraph) {
    $new_paragraph->setParentEntity($this->getEntity(), $this->getFieldName());
    $this->paragraphsReferenceField->appendItem(['entity' => $new_paragraph]);
    return $this;
  }

  /**
   * Delete a component.
   *
   * @param string $uuid
   *   The uuid of the component to delete.
   * @param bool $recursive
   *   Recursively delete child components.
   *
   * @return $this
   */
  public function deleteComponent(string $uuid, $recursive = FALSE) {
    if ($recursive) {
      $component = $this->getComponentByUuid($uuid);
      if ($component->isLayout()) {
        /** @var \Drupal\layout_paragraphs\LayoutParagraphsSection $section */
        $section = $this->getLayoutSection($component->getEntity());
        foreach ($section->getComponents() as $component) {
          $this->deleteComponent($component->getEntity()->uuid(), TRUE);
        }
      }
    }
    $delta = $this->getComponentDeltaByUuid($uuid);
    if (isset($this->paragraphsReferenceField[$delta])) {
      unset($this->paragraphsReferenceField[$delta]);
    }
    return $this;
  }

  /**
   * Searches for a component by its uuid and returns its delta.
   *
   * @param string $uuid
   *   The uuid to search for.
   *
   * @return int
   *   The component's delta, or -1 if no match.
   */
  protected function getComponentDeltaByUuid(string $uuid) {
    foreach ($this->paragraphsReferenceField as $key => $item) {
      if (isset($item->entity) && $item->entity->uuid() == $uuid) {
        return $key;
      }
    }
    return -1;
  }

  /**
   * {@inheritdoc}
   */
  public function getThirdPartySetting($provider, $key, $default = NULL) {
    return $this->thirdPartySettings[$provider][$key] ?? $default;
  }

  /**
   * {@inheritdoc}
   */
  public function getThirdPartySettings($provider) {
    return $this->thirdPartySettings[$provider] ?? [];
  }

  /**
   * {@inheritdoc}
   */
  public function setThirdPartySetting($provider, $key, $value) {
    $this->thirdPartySettings[$provider][$key] = $value;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function unsetThirdPartySetting($provider, $key) {
    unset($this->thirdPartySettings[$provider][$key]);
    // If the third party is no longer storing any information, completely
    // remove the array holding the settings for this provider.
    if (empty($this->thirdPartySettings[$provider])) {
      unset($this->thirdPartySettings[$provider]);
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getThirdPartyProviders() {
    return array_keys($this->thirdPartySettings);
  }

}

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

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