external_entity-1.0.x-dev/src/Entity/ExternalEntity.php

src/Entity/ExternalEntity.php
<?php

declare(strict_types=1);

namespace Drupal\external_entity\Entity;

use Drupal\Core\Cache\Cache;
use Drupal\Core\Utility\Error;
use Drupal\Core\Field\FieldItemList;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Component\Utility\DeprecationHelper;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\external_entity\Contracts\ExternalEntityInterface;
use Drupal\external_entity\Contracts\ExternalEntityTypeInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;

/**
 * Define the external entity.
 *
 * @EntityType(
 *    id = "external_entity",
 *    label = @Translation("External Entity"),
 *    bundle_entity_type = "external_entity_type",
 *    entity_keys = {
 *      "id" = "id",
 *      "uuid" = "uuid",
 *      "label" = "label"
 *    },
 *    handlers = {
 *      "storage" = "\Drupal\external_entity\Entity\ExternalEntityStorage",
 *      "access" = "\Drupal\external_entity\Entity\ExternalEntityAccessHandler",
 *      "views_data" = "\Drupal\external_entity\Entity\ExternalEntityViewsData",
 *      "view_builder" = "\Drupal\external_entity\Entity\ExternalEntityViewBuilder"
 *   }
 * )
 */
class ExternalEntity extends ExternalEntityBase implements ExternalEntityInterface {

  /**
   * @var string|null
   */
  protected ?string $type = null;

  /**
   * @var string|null
   */
  protected ?string $uuid = null;

  /**
   * @var string|null
   */
  protected ?string $label = null;

  /**
   * @var string|null
   */
  protected ?string $path = null;

  /**
   * @var string|null
   */
  protected ?string $resource = null;

  /**
   * @var string|null
   */
  protected ?string $variation = null;

  /**
   * @var array
   */
  protected array $fields = [];

  /**
   * @var array
   */
  protected array $properties = [];

  /**
   * @var array
   */
  protected array $fieldDefinitions = [];

  /**
   * @var array
   */
  protected array $processedProperties = [];

  /**
   * Define the external ID delimiter.
   */
  public const string ID_DELIMITER = '__';

  /**
   * {@inheritDoc}
   */
  public function id(): ?string {
    $uuid = $this->uuid();

    if (!isset($uuid)) {
      return NULL;
    }

    $delimiter = static::ID_DELIMITER;
    return "{$this->bundle()}{$delimiter}{$uuid}";
  }

  /**
   * {@inheritDoc}
   */
  public function bundle(): ?string {
    return $this->type;
  }

  /**
   * {@inheritDoc}
   */
  public function getPath(): ?string {
    return $this->path ?? NULL;
  }

  /**
   * {@inheritDoc}
   */
  public function getResource(): string {
    return $this->resource;
  }

  /**
   * {@inheritDoc}
   */
  public function getVariation(): string {
    return $this->variation;
  }

  /**
   * {@inheritDoc}
   */
  public function isNew(): bool {
    return FALSE;
  }

  /**
   * {@inheritDoc}
   */
  public function getProperties(bool $include_alteration = TRUE): array {
    if (empty($this->processedProperties)) {
      $this->processedProperties = $this->processProperties(
        $this->getPropertyDefinitions()
      );
    }

    return $this->processedProperties;
  }

  /**
   * {@inheritDoc}
   */
  public function getPropertyDefinitions(bool $include_alteration = TRUE): array {
    $properties = $this->properties;

    if ($include_alteration) {
      $this->alterResourceProperties($properties);
    }

    return $properties;
  }

  /**
   * {@inheritDoc}
   */
  public function getResourceProperties(): array {
    return $this->getBundleEntityType()->getResourceProperties(
      $this->getResource(),
      $this->getVariation()
    );
  }

  /**
   * {@inheritDoc}
   */
  public function getBundleEntityTypeId(): ?string {
    return $this->getEntityType()->getBundleEntityType();
  }

  /**
   * {@inheritDoc}
   */
  public function getBundleEntityType(): ?ExternalEntityTypeInterface {
    $bundle = $this->bundle();

    if (!isset($bundle)) {
      return NULL;
    }

    return $this->getBundleEntityTypeStorage()->load($bundle);
  }

  /**
   * {@inheritDoc}
   */
  public static function baseFieldDefinitions(
    EntityTypeInterface $entity_type,
  ): array {
    $fields = [];

    if ($entity_type->hasKey('id')) {
      $fields[$entity_type->getKey('id')] = BaseFieldDefinition::create('string')
        ->setLabel(new TranslatableMarkup('ID'))
        ->setReadOnly(TRUE)
        ->setSetting('unsigned', TRUE);
    }
    if ($entity_type->hasKey('uuid')) {
      $fields[$entity_type->getKey('uuid')] = BaseFieldDefinition::create('uuid')
        ->setLabel(new TranslatableMarkup('UUID'))
        ->setReadOnly(TRUE);
    }

    return $fields;
  }

  /**
   * {@inheritDoc}
   */
  public function getFieldDefinition($name): ?FieldDefinitionInterface {
    return $this->getFieldDefinitions()[$name] ?? NULL;
  }

  /**
   * {@inheritDoc}
   */
  public function getFieldDefinitions(): array {
    if (empty($this->fieldDefinitions)) {
      /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
      $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
      foreach ($this->getResourceProperties() as $name => $info) {
        if (
          !isset($info['type'])
          || !$field_type_manager->hasDefinition($info['type'])
        ) {
          continue;
        }
        $field_definition = BaseFieldDefinition::create($info['type']);
        $field_definition->setName($name);

        if ($label = $info['label'] ?? NULL) {
          $field_definition->setLabel($label);
        }

        if ($settings = $info['settings'] ?? []) {
          $field_definition->setSettings($settings);
        }

        $this->fieldDefinitions[$name] = $field_definition;
      }
    }

    return $this->fieldDefinitions;
  }

  /**
   * {@inheritDoc}
   */
  public function get($field_name): FieldItemListInterface {
    $fields = $this->getFields();

    if (!isset($fields[$field_name])) {
      throw new \InvalidArgumentException(sprintf('
        The %s field name is invalid.', $field_name
      ));
    }

    return $fields[$field_name];
  }

  /**
   * {@inheritDoc}
   */
  public function set($field_name, $value, $notify = TRUE): self {
    // Setting external entity field value is not supported at this time.
    return $this;
  }

  /**
   * {@inheritDoc}
   */
  public function getFields($include_computed = TRUE): array {
    if (!isset($this->fields) || empty($this->fields)) {
      $entity_adapter = $this->getTypedDataEntityAdapter();

      foreach ($this->getProperties() as $name => $info) {
        $definition = $this->getFieldDefinition($name);
        $item_class = $info['class'] ?? FieldItemList::class;
        if (
          !isset($definition, $info['value'])
          || !class_exists($item_class)
        ) {
          continue;
        }
        $item = $item_class::createInstance(
          $definition,
          $name,
          $entity_adapter
        );
        $item->setValue($info['value']);

        $this->fields[$name] = $item;
      }
    }

    return $this->fields;
  }

  /**
   * {@inheritDoc}
   */
  public function hasField($field_name): bool {
    return isset($this->getFields()[$field_name]);
  }

  /**
   * {@inheritDoc}
   */
  public function getCacheTagsToInvalidate(): array {
    $tags = [];

    try {
      $bundle = $this->getBundleEntityType();
      $display = $bundle->getResourceDisplay(
        $this->getResource(),
        $this->getVariation()
      );
      $tags = Cache::mergeTags(
        $bundle->getCacheTags(), $display->getCacheTags()
      );
    }
    catch (\Exception $exception) {
      DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '10.1.0', fn() => Error::logException(\Drupal::logger('external_entity'), $exception), fn() => watchdog_exception('external_entity', $exception));
    }

    return Cache::mergeTags([
      "{$this->entityTypeId}:{$this->uuid()}",
      "{$this->entityTypeId}:bundle:{$this->bundle()}",
      "{$this->entityTypeId}:resource:{$this->getResource()}",
    ], $tags);
  }

  /**
   * Alter the resource properties.
   *
   * @param array $properties
   *   An array of resource properties.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function alterResourceProperties(array &$properties): void {
    foreach ($this->getResourcePropertyAlterations() as $resource) {
      if (!isset($properties[$resource->getProperty()])) {
        continue;
      }
      $property_value = &$properties[$resource->getProperty()]['value'];

      if (is_array($property_value)) {
        foreach ($property_value as &$value) {
          foreach ($resource->getAlterations() as $key => $altered_value) {
            $value[$key] = $altered_value;
          }
        }
      }
      else {
        foreach ($resource->getAlterations() as $key => $altered_value) {
          $property_value[$key] = $altered_value;
        }
      }
    }
  }

  /**
   * Get current resource property alterations.
   *
   * @return \Drupal\external_entity\Entity\ExternalEntityResourceAlter[]
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getResourcePropertyAlterations(): array {
    return $this->getBundleEntityType()->getResourceAlterations(
      $this->getResource(),
      $this->getVariation()
    );
  }

  /**
   * Process the external entity properties.
   *
   * @param array $properties
   *   An array of property definitions.
   *
   * @return array
   *   An array of the processed properties.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\external_entity\Exception\NotFoundResourceDisplayException
   */
  protected function processProperties(array $properties): array {
    foreach ($properties as &$property) {
      if (
        !isset($property['value'], $property['type'])
        || empty($property['value'])
      ) {
        continue;
      }

      $this->preparePropertyValue($property['type'], $property['value']);
    }

    return $properties;
  }

  /**
   * Prepare the external entity property value.
   *
   * @param string $type
   *   The external entity property type.
   * @param array $value
   *   An array of the external entity property values.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\external_entity\Exception\NotFoundResourceDisplayException
   */
  protected function preparePropertyValue(
    string $type,
    array &$value,
  ): void {
    if ($this->isFieldReference($type)) {
      foreach ($value as &$data) {
        if (!isset($data['target_uuid'])) {
          continue;
        }
        $target_uuid = $data['target_uuid'];

        $data['target_id'] = NULL;
        $data['target_type'] = 'external_entity';

        if (isset($data['target_revision_id'])) {
          $data['target_revision_id'] = NULL;
        }
        $identifier = "{$this->bundle()}__{$target_uuid}";
        unset($data['target_uuid']);

        if ($external_entity = $this->loadEntity($identifier)) {
          /** @var \Drupal\external_entity\Entity\ExternalEntityViewBuilder $view_builder */
          $view_builder = $this->entityTypeManager()->getViewBuilder(
            $external_entity->getEntityTypeId()
          );

          $data['entity'] = $view_builder->createProxyEntity($external_entity);
        }
      }
    }
  }

  /**
   * Load an external entity.
   *
   * @param string $identifier
   *   The external entity identifier.
   *
   * @return \Drupal\Core\Entity\EntityInterface|null
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function loadEntity(string $identifier): ?EntityInterface {
    $storage = $this->entityTypeManager()->getStorage('external_entity');
    return $storage->load($identifier);
  }

  /**
   * Check if a field type is a reference entity.
   *
   * @param string $type
   *   An entity field type.
   *
   * @return bool
   *   Return TRUE if the field type is a reference; otherwise FALSE.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function isFieldReference(string $type): bool {
    if (!$this->fieldTypeManager()->hasDefinition($type)) {
      return FALSE;
    }
    $base = EntityReferenceItem::class;

    $class = $this->fieldTypeManager()->getPluginClass($type);

    return $class === $base || is_subclass_of($class, $base);
  }

  /**
   * Get the typed data entity adapter.
   *
   * @return \Drupal\Core\TypedData\TypedDataInterface
   *   The typed data entity adapter.
   */
  protected function getTypedDataEntityAdapter(): TypedDataInterface {
    $typed_data_manager = $this->typedDataManager();

    return $typed_data_manager->create(
      $typed_data_manager->createDataDefinition('entity'),
      $this,
      'entity'
    );
  }

  /**
   * Get the bundle entity type storage.
   *
   * @return \Drupal\Core\Entity\EntityStorageInterface
   *   The bundle entity type storage instance.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getBundleEntityTypeStorage(): EntityStorageInterface {
    return $this->entityTypeManager()->getStorage($this->getBundleEntityTypeId());
  }

  /**
   * Typed data manager service.
   *
   * @return \Drupal\Core\TypedData\TypedDataManagerInterface
   *   The typed data manager instance.
   */
  protected function typedDataManager(): TypedDataManagerInterface {
    return \Drupal::service('typed_data_manager');
  }

  /**
   * Field type manager.
   *
   * @return \Drupal\Core\Field\FieldTypePluginManagerInterface
   *   The field type manager.
   */
  protected function fieldTypeManager(): FieldTypePluginManagerInterface {
    return \Drupal::service('plugin.manager.field.field_type');
  }

}

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

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