external_entities-8.x-2.x-dev/modules/xntt_file_field/src/ExternalFileStorage.php

modules/xntt_file_field/src/ExternalFileStorage.php
<?php

namespace Drupal\xntt_file_field;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\MemoryCache\MemoryCacheInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\ContentEntityStorageBase;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\file\FileInterface;
use Drupal\xntt_file_field\Entity\ExternalFile;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\external_entities\ExternalEntityStorageInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * An external file "storage" implementation.
 *
 * This class does not really store external files but rather provide a mean to
 * manage them virtually.
 */
class ExternalFileStorage extends ContentEntityStorageBase implements ExternalFileStorageInterface {

  /**
   * Entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The logger channel factory.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $loggerChannelFactory;

  /**
   * The field mapper plugin logger channel.
   *
   * @var \Drupal\Core\Logger\LoggerChannel
   */
  protected $logger;

  /**
   * Constructs a ExternalFileStorage object.
   *
   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
   *   The entity type definition.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
   *   The entity field manager.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   The cache backend to be used.
   * @param \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface $memory_cache
   *   The memory cache backend.
   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
   *   The entity type bundle info.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager service.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
   *   The logger channel factory.
   */
  public function __construct(
    EntityTypeInterface $entity_type,
    EntityFieldManagerInterface $entity_field_manager,
    CacheBackendInterface $cache,
    MemoryCacheInterface $memory_cache,
    EntityTypeBundleInfoInterface $entity_type_bundle_info,
    EntityTypeManagerInterface $entity_type_manager,
    LoggerChannelFactoryInterface $logger_factory,
  ) {
    parent::__construct($entity_type, $entity_field_manager, $cache, $memory_cache, $entity_type_bundle_info);
    $this->entityTypeManager = $entity_type_manager;
    $this->loggerChannelFactory = $logger_factory;
    $this->logger = $this->loggerChannelFactory->get('xntt_file_field');
  }

  /**
   * {@inheritdoc}
   */
  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
    return new static(
      $entity_type,
      $container->get('entity_field.manager'),
      $container->get('cache.entity'),
      $container->get('entity.memory_cache'),
      $container->get('entity_type.bundle.info'),
      $container->get('entity_type.manager'),
      $container->get('logger.factory')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getRealUri(
    string $xntt_type,
    string $xntt_id,
    string $file_field_name,
    int $file_delta = 0,
    ?ExternalEntityStorageInterface &$storage = NULL,
    ?array &$fm_config = NULL,
  ) {
    if (empty($storage)) {
      $storage = $this->entityTypeManager->getStorage($xntt_type);
      if (!is_a($storage, ExternalEntityStorageInterface::class)) {
        return NULL;
      }
    }
    $xntt = $storage->load($xntt_id);
    if (empty($xntt)) {
      return NULL;
    }
    try {
      $fm_config ??= $xntt
        ->getExternalEntityType()
        ->getFieldMapperConfig($file_field_name);
      if (empty($fm_config)) {
        throw new \InvalidArgumentException("No field mapper config found for field '$file_field_name' (external entity '$xntt_type')");
      }
      // Get the real URI: we need to use the external entity ::toArray()
      // method to get the real URI as "real_uri" is not a real property
      // of the file field (but added by external entities).
      // @todo This could be optimized to avoid generating a full array.
      $real_uri = $xntt
        ->toArray()[$file_field_name][$file_delta]['real_uri'] ?? NULL;
    }
    catch (\InvalidArgumentException $e) {
      // Output a message to let know the mapping failed.
      $this->logger->warning(
        'Failed to get mapped URI. '
        . $e
      );
      return NULL;
    }

    // Filter URI if needed.
    if (!empty($fm_config['filter'])) {
      $match = preg_match(
        '#^'
        . $fm_config['filter']
        . '$#',
        $real_uri
      );
      if (FALSE === $match) {
        // Invalid regexp.
        $this->logger->warning(
          'Invalid regular expression for external file filtering (field "$file_field_name" of "$xntt_type"): '
          . $fm_config['filter']
        );
        return NULL;
      }
      elseif (0 === $match) {
        // Did not pass filtering.
        $this->logger->warning(
          'External file URI did not pass filtering (field "$file_field_name" of "$xntt_type"): '
          . $real_uri
        );
        return NULL;
      }
    }

    return $real_uri;
  }

  /**
   * {@inheritdoc}
   */
  public function loadExternalFile(string $id) :ExternalFile {
    $file_entity = NULL;
    if (preg_match(static::XNTT_FILE_ID_REGEX, $id, $matches)) {
      try {
        [, $xntt_type, $xntt_id, $file_field_name] = $matches;
        $file_delta = $matches[4] ?? 0;
        $storage = $this->entityTypeManager->getStorage($xntt_type);
        if (!is_a($storage, ExternalEntityStorageInterface::class)) {
          throw new EntityStorageException(
            'ExternalFileStorage: invalid external entity type "' . $xntt_type . '"'
          );
        }
        $real_uri = $this->getRealUri(
          $xntt_type,
          $xntt_id,
          $file_field_name,
          $file_delta,
          $storage,
          $fm_config
        );

        // Make sure we got a URI.
        if (!empty($real_uri)) {

          // Extract file name.
          if (preg_match('#([^\\/\?]+)(?:\?.*)?$#', $real_uri, $matches)) {
            $filename = $matches[1];
          }
          else {
            // Default file name.
            $filename = 'xnttfile';
          }
          // Append specified extension if one.
          if (!empty($fm_config['extension'])) {
            $filename .=
              '.'
              . $fm_config['extension'];
          }
          $file_data = [
            // Use the xntt stream URI.
            'uri' =>
            'xntt://'
            . $xntt_type
            . '/'
            . $xntt_id
            . '/'
            . $file_field_name
            . '/'
            . $file_delta
            . '/'
            . $filename,
            'real_uri' => $real_uri,
            'filename' => $filename,
          ];
          ExternalFile::preCreate($storage, $file_data);
          $file_entity = ExternalFile::create($file_data);
          $file_entity->set('fid', $id);
          $file_entity->setPermanent();
        }
      }
      catch (PluginNotFoundException $e) {
        // Just ignore.
      }
    }

    // Did we fail to load something?
    if (empty($file_entity)) {
      throw new EntityStorageException(
        'ExternalFileStorage: invalid identifier "' . $id . '"'
      );
    }

    return $file_entity;
  }

  /**
   * {@inheritdoc}
   */
  protected function doLoadMultiple(?array $ids = NULL) {
    // Attempt to load entities from the persistent cache. This will remove IDs
    // that were loaded from $ids.
    $entities_from_cache = $this->getFromPersistentCache($ids);

    // Load any remaining entities from the database.
    if ($entities_from_storage = $this->getFromStorage($ids)) {
      $this->invokeStorageLoadHook($entities_from_storage);
      $this->setPersistentCache($entities_from_storage);
    }

    return $entities_from_cache + $entities_from_storage;
  }

  /**
   * Gets entities from the storage.
   *
   * @param array|null $ids
   *   If not empty, return entities that match these IDs. Return all entities
   *   when NULL.
   *
   * @return \Drupal\Core\Entity\ContentEntityInterface[]
   *   Array of entities from the storage.
   */
  protected function getFromStorage(?array $ids = NULL) {
    if (!isset($ids)) {
      return [];
    }

    $entities = [];
    foreach ($ids as $id) {
      try {
        $entities[$id] = static::loadExternalFile($id);
      }
      catch (EntityStorageException $e) {
        // Failed to load, just ignore.
      }
    }
    return $entities;
  }

  /**
   * {@inheritdoc}
   */
  protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definition, $batch_size) {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition) {
  }

  /**
   * {@inheritdoc}
   */
  protected function cleanIds(array $ids, $entity_key = 'id') {
    return $ids;
  }

  /**
   * {@inheritdoc}
   */
  protected function doLoadMultipleRevisionsFieldItems($revision_ids) {
    return [];
  }

  /**
   * {@inheritdoc}
   */
  protected function doDeleteRevisionFieldItems(ContentEntityInterface $revision) {
  }

  /**
   * {@inheritdoc}
   */
  public function delete(array $entities) {
  }

  /**
   * {@inheritdoc}
   */
  protected function doDeleteFieldItems($entities) {
  }

  /**
   * {@inheritdoc}
   */
  public function save(EntityInterface $entity) {
  }

  /**
   * {@inheritdoc}
   */
  public function restore(EntityInterface $entity) {
  }

  /**
   * {@inheritdoc}
   */
  protected function doSaveFieldItems(ContentEntityInterface $entity, array $names = []) {
  }

  /**
   * {@inheritdoc}
   */
  protected function has($id, EntityInterface $entity) {
    return !$entity->isNew();
  }

  /**
   * {@inheritdoc}
   */
  protected function getQueryServiceName() {
    return 'entity.query.xntt_file_field';
  }

  /**
   * {@inheritdoc}
   */
  public function countFieldData($storage_definition, $as_bool = FALSE) {
    return $as_bool ? FALSE : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function spaceUsed($uid = NULL, $status = FileInterface::STATUS_PERMANENT) {
    return 0;
  }

  /**
   * Tells if a given file instance is an external file.
   *
   * @param \Drupal\file\FileInterface $file
   *   A file instance.
   *
   * @return bool
   *   TRUE if the file is an external file, FALSE otherwise.
   */
  public static function isExternalFile(FileInterface $file) :bool {
    return (bool) preg_match(static::XNTT_FILE_ID_REGEX, $file->id());
  }

}

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

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