wse-1.0.x-dev/modules/wse_config/src/WseConfigDatabaseStorage.php

modules/wse_config/src/WseConfigDatabaseStorage.php
<?php

namespace Drupal\wse_config;

use Drupal\Core\Config\StorageInterface;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\Utility\Error;

/**
 * Defines the Database storage.
 */
class WseConfigDatabaseStorage implements StorageInterface {

  use DependencySerializationTrait;
  use LoggerChannelTrait;

  /**
   * The decorated config.storage service.
   *
   * @var \Drupal\Core\Config\DatabaseStorage
   */
  protected $inner;

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

  /**
   * The workspace ID detector.
   *
   * @var \Drupal\wse_config\WorkspaceIdDetector
   */
  protected $workspaceIdDetector;

  /**
   * The wse config matcher.
   *
   * @var \Drupal\wse_config\WseConfigMatcher
   */
  protected $configMatcher;

  /**
   * The storage collection.
   *
   * @var string
   */
  protected $collection = StorageInterface::DEFAULT_COLLECTION;

  /**
   * Constructs a new WseConfigDatabaseStorage.
   *
   * @param \Drupal\Core\Config\StorageInterface $inner
   *   The decorated config.storage.active service.
   * @param \Drupal\wse_config\WorkspaceIdDetector $workspace_id_detector
   *   The workspace ID detector.
   * @param \Drupal\wse_config\WseConfigMatcher $config_matcher
   *   The wse config matcher.
   * @param string $collection
   *   (optional) The collection to store configuration in. Defaults to the
   *   default collection.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function __construct(StorageInterface $inner, WorkspaceIdDetector $workspace_id_detector, WseConfigMatcher $config_matcher, $collection = StorageInterface::DEFAULT_COLLECTION) {
    $this->inner = $inner->createCollection($collection);
    $this->workspaceIdDetector = $workspace_id_detector;
    $this->configMatcher = $config_matcher;
    $this->collection = $collection;
  }

  /**
   * {@inheritdoc}
   */
  public function exists($name) {
    if (!$this->wseConfigStorageIsActive() || $this->configMatcher->configIsIgnored([$name])) {
      return $this->inner->exists($name);
    }

    try {
      $exists = (bool) $this->getWorkspaceAwareQuery()
        ->condition('collection', $this->getCollectionName())
        ->condition('name', $name)
        ->execute();

      // Cover the case where this module is active, but existing config was not
      // yet saved into content entities.
      if (!$exists) {
        $exists = $this->inner->exists($name);
      }
      return $exists;
    }
    catch (\Exception $e) {
      Error::logException($this->getLogger('wse_config'), $e);
      return FALSE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function read($name) {
    if (!$this->wseConfigStorageIsActive() || $this->configMatcher->configIsIgnored([$name])) {
      return $this->inner->read($name);
    }

    $data = FALSE;
    try {
      $entity = $this->getWseConfigEntityByName($name);
      if ($entity && $entity->get('data')->value) {
        $data = $this->decode($entity->get('data')->value);
      }

      // Fall back to inner storage if no config was saved in a content entity.
      if (!$data) {
        $data = $this->inner->read($name);
      }
    }
    catch (\Exception $e) {
      Error::logException($this->getLogger('wse_config'), $e);

      // If something goes wrong with reading config from wse_config entities,
      // fall back to the core storage or things will break.
      $data = $this->inner->read($name);
    }
    return $data;
  }

  /**
   * {@inheritdoc}
   */
  public function readMultiple(array $names) {
    if (!$this->wseConfigStorageIsActive()) {
      return $this->inner->readMultiple($names);
    }

    $list = [];
    try {
      $not_ignored = $this->getUnIgnored($names);
      if ($not_ignored) {
        $query = $this->getWorkspaceAwareQuery()
          ->condition('name', $not_ignored, 'IN');

        if ($this->getCollectionName()) {
          $query->condition('collection', $this->collection);
        }

        $result = $query->execute();
        $revisions = $this->getWseConfigStorage()->loadMultiple($result);

        foreach ($revisions as $revision) {
          // Load the translation config override record if we find a collection
          // tagged like langcode.de.
          if (\str_contains($this->collection, 'langcode.')) {
            [, $langcode] = explode('.', $this->collection);
            $revision = $revision->getTranslation($langcode);
          }
          $name = $revision->get('name')->value;
          $list[$name] = $this->decode(
            $revision->get('data')->value
          );
        }
      }

      // At this point, the list contains only config which was already saved
      // inside a wse config entity, but not all the other configs which may
      // have existed before enabling the module. Thus add the latter ones in,
      // and also add ignored ones if there were any.
      $list += $this->inner->readMultiple($names);
    }
    catch (\Exception $e) {
      Error::logException($this->getLogger('wse_config'), $e);

      // If something goes wrong with reading config from wse_config entities,
      // fall back to the core storage or things will break.
      $list = $this->inner->readMultiple($names);
    }
    return $list;
  }

  /**
   * {@inheritdoc}
   */
  public function write($name, array $data, $langcode = NULL) {
    if (!$this->wseConfigStorageIsActive() || $this->configMatcher->configIsIgnored([$name])) {
      return $this->inner->write($name, $data);
    }

    $serialized_data = $this->encode($data);
    try {
      $storage = $this->getWseConfigStorage();
      $entity = $this->getWseConfigEntityByName($name);
      if (!$entity) {
        // Create a default revision with the active configuration data, needed
        // in order to have a canonical version to revert to.
        $default_revision_data = $serialized_data;
        if ($existing_data = $this->inner->read($name)) {
          $default_revision_data = $this->encode($existing_data);
        }
        $entity = \Drupal::service('workspaces.manager')->executeOutsideWorkspace(function () use ($storage, $name, $default_revision_data) {
          $entity = $storage->create([
            'name' => $name,
            'collection' => $this->getCollectionName(),
            'data' => $default_revision_data,
          ]);
          $entity->save();

          return $entity;
        });
      }

      if ($langcode) {
        if ($entity->hasTranslation($langcode)) {
          $entity = $entity->getTranslation($langcode);
        }
        else {
          $entity = $entity->addTranslation($langcode);
        }
      }

      $entity->set('name', $name);
      $entity->set('collection', $this->getCollectionName());
      $entity->set('data', $serialized_data);
      $entity->save();

      return TRUE;
    }
    catch (\Exception $e) {
      Error::logException($this->getLogger('wse_config'), $e);
      // Some other failure that we can not recover from.
      throw $e;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function delete($name) {
    if (!$this->wseConfigStorageIsActive() || $this->configMatcher->configIsIgnored([$name])) {
      return $this->inner->delete($name);
    }

    try {
      $entity = $this->getWseConfigEntityByName($name);
      $entity->delete();
      return TRUE;
    }
    catch (\Exception $exception) {
      return FALSE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function rename($name, $new_name) {
    if (!$this->wseConfigStorageIsActive() || $this->configMatcher->configIsIgnored([$name])) {
      return $this->inner->rename($name, $new_name);
    }

    try {
      $entity = $this->getWseConfigEntityByName($name);
      $entity->set('name', $new_name)->save();
      return TRUE;
    }
    catch (\Exception $exception) {
      return FALSE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function encode($data) {
    return $this->inner->encode($data);
  }

  /**
   * {@inheritdoc}
   */
  public function decode($raw) {
    return $this->inner->decode($raw);
  }

  /**
   * {@inheritdoc}
   */
  public function listAll($prefix = '') {
    if (!$this->wseConfigStorageIsActive()) {
      return $this->inner->listAll($prefix);
    }

    try {
      $configs = $this->listAllWseConfigs($prefix);

      // Add configs which were not saved into wse_config entities yet or that
      // are ignored from the inner storage.
      $configs = array_merge($this->inner->listAll($prefix), $configs);
      return $configs;
    }
    catch (\Exception $e) {
      Error::logException($this->getLogger('wse_config'), $e);
      return [];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function deleteAll($prefix = '') {
    if (!$this->wseConfigStorageIsActive()) {
      return $this->inner->deleteAll($prefix);
    }

    try {
      $not_ignored = $this->getUnIgnored([$prefix]);
      if ($not_ignored) {
        $query = $this->getWorkspaceAwareQuery()
          ->condition('collection', $this->getCollectionName())
          ->condition('name', $not_ignored, 'IN');
        $result = $query->execute();

        if ($result) {
          $entities = $this->getWseConfigStorage()->loadMultiple($result);
          foreach ($entities as $entity) {
            $entity->delete();
          }
        }
      }
    }
    catch (\Exception $e) {
      Error::logException($this->getLogger('wse_config'), $e);
      return FALSE;
    }

    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function createCollection($collection) {
    return new static(
      $this->inner,
      $this->workspaceIdDetector,
      $this->configMatcher,
      $collection
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getCollectionName() {
    return $this->collection;
  }

  /**
   * {@inheritdoc}
   */
  public function getAllCollectionNames() {
    if (!$this->wseConfigStorageIsActive()) {
      return $this->inner->getAllCollectionNames();
    }

    try {
      $result = $this->getWorkspaceAwareQuery()
        ->condition('collection', $this->getCollectionName(), '<>')
        ->sort('collection')
        ->execute();

      $collections = [];
      if ($result) {
        $entities = $this->getWseConfigStorage()->loadMultiple($result);
        foreach ($entities as $entity) {
          $collection = $entity->get('collection')->value;
          if (!in_array($collection, $collections)) {
            $collections[] = $collection;
          }
        }
      }

      $collections = array_merge($this->inner->getAllCollectionNames(), $collections);
      return $collections;
    }
    catch (\Exception $e) {
      Error::logException($this->getLogger('wse_config'), $e);
      // @todo does this need to return the collection names of core storage
      // instead?
      return [];
    }
  }

  /**
   * Loads a wse config entity by name.
   *
   * @param string $name
   *   The name of the entity to be loaded.
   *
   * @return \Drupal\Core\Entity\EntityInterface|false
   *   A loaded entity or false if none was found for given name.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getWseConfigEntityByName($name) {
    $entities = $this->getWseConfigEntitiesByNames([$name]);
    return reset($entities);
  }

  /**
   * Loads wse config entity by names.
   *
   * @param array $names
   *   The names of the entities to be loaded.
   *
   * @return \Drupal\Core\Entity\EntityInterface[]
   *   Loaded entities.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getWseConfigEntitiesByNames(array $names) {
    $result = $this->getWorkspaceAwareQuery()
      ->condition('name', $names, 'IN')
      ->execute();
    return $this->getWseConfigStorage()->loadMultiple($result);
  }

  /**
   * Checks if using wse_config storage is applicable.
   *
   * If the module hasn't completed the install process we can't use this
   * storage yet, thus the first condition. We neither need to use it if there
   * is no workspace active, see second condition.
   *
   * @return bool
   *   TRUE if wse_config storage should be used.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function wseConfigStorageIsActive() {
    return $this->workspaceIdDetector->getActiveWorkspaceId();
  }

  /**
   * Shortcut for the entity storage of wse_config entity storage.
   *
   * @return \Drupal\Core\Entity\EntityStorageInterface
   *   The wse_config entity storage.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getWseConfigStorage() {
    return $this->getEntityTypeManager()->getStorage('wse_config');
  }

  /**
   * Extracts configs which are not ignored from a given list of config names.
   *
   * @param array $names
   *   The list of config names to extract the not ignored configs from.
   *
   * @return array
   *   List of config names which are not ignored.
   *
   * @see \Drupal\wse_config\Event\WseConfigEvents
   * @see \Drupal\wse_config\Event\WseConfigOptOutEvent
   * @see \Drupal\wse_config\WseConfigMatcher
   */
  public function getUnIgnored(array $names) {
    $ignored = $this->configMatcher->getIgnoredConfigs($names);
    return array_diff($names, $ignored);
  }

  /**
   * Writes all config stored in wse_config entities into the inner storage.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function publishWseConfig() {
    $configs = $this->listAllWseConfigs();
    foreach ($configs as $config_name) {
      $entity = $this->getWseConfigEntityByName($config_name);
      foreach ($entity->getTranslationLanguages() as $langcode => $language) {
        $translation = $entity->getTranslation($langcode);
        if ($translation && $translation->get('data')->value) {
          // Pass the collection to the inner storage.
          if ($collection = $translation->collection->value) {
            $original_collection = $this->inner->getCollectionName();
            $this->inner = $this->inner->createCollection($collection);
          }
          $data = $this->decode($translation->get('data')->value);
          $this->inner->write($config_name, $data);
          if ($collection && isset($original_collection)) {
            // Put the collection back how we found it as it will persist for
            // any other entities being operated on.
            $this->inner = $this->inner->createCollection($original_collection);
          }
        }
      }
    }
  }

  /**
   * Writes config data stored in wse_config entities into the inner storage.
   */
  public function revertWseConfig(array $revision_ids) {
    $wse_config_revisions = $this->getWseConfigStorage()->loadMultipleRevisions($revision_ids);
    foreach ($wse_config_revisions as $revision) {
      $name = $revision->get('name')->value;
      $data = $this->decode($revision->get('data')->value);
      $this->inner->write($name, $data);
    }
  }

  /**
   * Retrieves a list of configs that were changed in the current workspace.
   *
   * @param string $prefix
   *   The prefix to filter by. If omitted, all configuration object names that
   *   exist are returned.
   *
   * @return array
   *   List of config names.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function listAllWseConfigs($prefix = '') {
    $configs = [];
    $not_ignored = $this->getUnIgnored([$prefix]);

    if (count($not_ignored)) {
      $query = $this->getWorkspaceAwareQuery()
        ->sort('collection')
        ->sort('name');

      $not_ignored = array_filter($not_ignored);
      if ($not_ignored) {
        $or_group = $query->orConditionGroup();
        foreach ($not_ignored as $prefix) {
          $or_group->condition('name', $prefix, 'STARTS_WITH');
        }
        $query->condition($or_group);
      }

      if ($this->getCollectionName()) {
        $query->condition('collection', $this->getCollectionName());
      }

      $result = $query->execute();
      if ($result) {
        $entities = $this->getWseConfigStorage()->loadMultiple($result);
        foreach ($entities as $entity) {
          $configs[] = $entity->get('name')->value;
        }
      }
    }
    return $configs;
  }

  /**
   * Provides the wse_config entity query with a condition on workspace.
   *
   * @return \Drupal\Core\Entity\Query\QueryInterface
   *   The entity query.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function getWorkspaceAwareQuery() {
    return $this->getWseConfigStorage()->getQuery()
      ->accessCheck(FALSE)
      ->condition('workspace', $this->workspaceIdDetector->getActiveWorkspaceId());
  }

  /**
   * Gets the entity type manager.
   */
  protected function getEntityTypeManager() {
    if (!$this->entityTypeManager) {
      $this->entityTypeManager = \Drupal::entityTypeManager();
    }
    return $this->entityTypeManager;
  }

}

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

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