class RecipeMultipleModulesConfigStorage

Same name and namespace in other branches
  1. 11.x core/lib/Drupal/Core/Recipe/RecipeMultipleModulesConfigStorage.php \Drupal\Core\Recipe\RecipeMultipleModulesConfigStorage

Provides a read-only config storage spanning multiple modules' config.

When the recipe runner installs modules, it puts \Drupal\Core\Config\ConfigInstaller into config sync mode. In sync mode, \Drupal\Core\Config\ConfigInstaller::installDefaultConfig() only installs simple configuration from modules; config entities are skipped because they are handled later by the recipe installer in \Drupal\Core\Recipe\RecipeRunner::processConfiguration().

This storage is used as the source storage for the config installer during module installation. It combines the config/install directories of all the modules being installed together, keyed by module name. It ensures that configuration is only read from the module that provides the configuration (based on the configuration name prefix matching the module name). This prevents a module from overriding another module's configuration during a multi-module install.

@internal This API is experimental.

Hierarchy

Expanded class hierarchy of RecipeMultipleModulesConfigStorage

See also

\Drupal\Core\Recipe\RecipeRunner::installModules()

\Drupal\Core\Config\ConfigInstaller::installDefaultConfig()

1 file declares its use of RecipeMultipleModulesConfigStorage
RecipeMultipleModulesConfigStorageTest.php in core/tests/Drupal/Tests/Core/Recipe/RecipeMultipleModulesConfigStorageTest.php

File

core/lib/Drupal/Core/Recipe/RecipeMultipleModulesConfigStorage.php, line 36

Namespace

Drupal\Core\Recipe
View source
final class RecipeMultipleModulesConfigStorage implements StorageInterface {
  
  /**
   * Constructs a RecipeMultipleModulesConfigStorage.
   *
   * @param array<string, \Drupal\Core\Config\FileStorage> $fileStorages
   *   The file storages for each module, keyed by the module name.
   * @param string $collection
   *   (optional) The collection to read configuration from. Defaults to the
   *   default collection.
   */
  private function __construct(private readonly array $fileStorages, private readonly string $collection = StorageInterface::DEFAULT_COLLECTION) {
  }
  
  /**
   * Creates a RecipeMultipleModulesConfigStorage from a list of modules.
   *
   * @param string[] $modules
   *   The list of modules.
   * @param \Drupal\Core\Extension\ModuleExtensionList $extensionList
   *   The extension listing service.
   * @param string $collection
   *   (optional) The collection to read configuration from. Defaults to the
   *   default collection.
   *
   * @return self
   *   The RecipeMultipleModulesConfigStorage object.
   */
  public static function createFromModuleList(array $modules, ModuleExtensionList $extensionList, string $collection = StorageInterface::DEFAULT_COLLECTION) : self {
    if (empty($modules)) {
      throw new \InvalidArgumentException('At least one module must be provided.');
    }
    // Convert the list of modules to a list of file storages keyed by the
    // module name.
    $file_storages = array_map(fn($module) => new FileStorage($extensionList->get($module)
      ->getPath() . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection), array_combine($modules, $modules));
    return new self($file_storages, $collection);
  }
  
  /**
   * Gets the correct module configuration storage to use.
   *
   * @param string $name
   *   The name of a configuration object to get the storage for.
   *
   * @return \Drupal\Core\Config\FileStorage|null
   *   The storage to use.
   */
  private function getStorage(string $name) : ?FileStorage {
    [$module] = explode('.', $name, 2);
    return $this->fileStorages[$module] ?? NULL;
  }
  
  /**
   * {@inheritdoc}
   */
  public function exists($name) : bool {
    return $this->getStorage($name)?->exists($name) ?? FALSE;
  }
  
  /**
   * {@inheritdoc}
   */
  public function read($name) : array|false {
    return $this->getStorage($name)?->read($name) ?? FALSE;
  }
  
  /**
   * {@inheritdoc}
   */
  public function readMultiple(array $names) : array {
    $names_by_module = [];
    foreach ($names as $name) {
      [$module] = explode('.', $name, 2);
      if (isset($this->fileStorages[$module])) {
        $names_by_module[$module][] = $name;
      }
    }
    $data = [];
    foreach ($names_by_module as $module => $name_list) {
      $data = array_merge($this->fileStorages[$module]
        ->readMultiple($name_list), $data);
    }
    return $data;
  }
  
  /**
   * {@inheritdoc}
   */
  public function write($name, array $data) : never {
    throw new \BadMethodCallException();
  }
  
  /**
   * {@inheritdoc}
   */
  public function delete($name) : never {
    throw new \BadMethodCallException();
  }
  
  /**
   * {@inheritdoc}
   */
  public function rename($name, $new_name) : never {
    throw new \BadMethodCallException();
  }
  
  /**
   * {@inheritdoc}
   */
  public function encode($data) : string {
    return array_first($this->fileStorages)
      ->encode($data);
  }
  
  /**
   * {@inheritdoc}
   */
  public function decode($raw) : array {
    return array_first($this->fileStorages)
      ->decode($raw);
  }
  
  /**
   * {@inheritdoc}
   */
  public function listAll($prefix = '') : array {
    // Optimization: if the prefix contains a dot only look in a single storage.
    if (str_contains($prefix, '.')) {
      [$module] = explode('.', $prefix, 2);
      return $this->getStorage($module)?->listAll($prefix) ?? [];
    }
    // If the prefix is empty or doesn't contain a dot, list all the
    // configuration in the module storages that begin with the module's name.
    $names = [];
    foreach ($this->fileStorages as $module => $fileStorage) {
      // Optimization: if the prefix does not match the module name, skip it.
      if ($prefix === '' || str_starts_with($module, $prefix)) {
        $names = array_merge($fileStorage->listAll($module . '.'), $names);
      }
    }
    if ($prefix !== '') {
      // Filter out the names that don't start with the prefix.
      $names = array_filter($names, fn(string $name) => str_starts_with($name, $prefix));
    }
    sort($names);
    return $names;
  }
  
  /**
   * {@inheritdoc}
   */
  public function deleteAll($prefix = '') : never {
    throw new \BadMethodCallException();
  }
  
  /**
   * {@inheritdoc}
   */
  public function createCollection($collection) : self {
    $file_storages = array_map(fn(FileStorage $fileStorage) => $fileStorage->createCollection($collection), $this->fileStorages);
    return new self($file_storages, $collection);
  }
  
  /**
   * {@inheritdoc}
   */
  public function getAllCollectionNames() : array {
    $names = [];
    foreach ($this->fileStorages as $fileStorage) {
      $names = array_merge($names, $fileStorage->getAllCollectionNames());
    }
    return array_values(array_unique($names));
  }
  
  /**
   * {@inheritdoc}
   */
  public function getCollectionName() : string {
    return $this->collection;
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title
RecipeMultipleModulesConfigStorage::createCollection public function Creates a collection on the storage. Overrides StorageInterface::createCollection
RecipeMultipleModulesConfigStorage::createFromModuleList public static function Creates a RecipeMultipleModulesConfigStorage from a list of modules.
RecipeMultipleModulesConfigStorage::decode public function Decodes configuration data from the storage-specific format. Overrides StorageInterface::decode
RecipeMultipleModulesConfigStorage::delete public function Deletes a configuration object from the storage. Overrides StorageInterface::delete
RecipeMultipleModulesConfigStorage::deleteAll public function Deletes configuration objects whose names start with a given prefix. Overrides StorageInterface::deleteAll
RecipeMultipleModulesConfigStorage::encode public function Encodes configuration data into the storage-specific format. Overrides StorageInterface::encode
RecipeMultipleModulesConfigStorage::exists public function Returns whether a configuration object exists. Overrides StorageInterface::exists
RecipeMultipleModulesConfigStorage::getAllCollectionNames public function Gets the existing collections. Overrides StorageInterface::getAllCollectionNames
RecipeMultipleModulesConfigStorage::getCollectionName public function Gets the name of the current collection the storage is using. Overrides StorageInterface::getCollectionName
RecipeMultipleModulesConfigStorage::getStorage private function Gets the correct module configuration storage to use.
RecipeMultipleModulesConfigStorage::listAll public function Gets configuration object names starting with a given prefix. Overrides StorageInterface::listAll
RecipeMultipleModulesConfigStorage::read public function Reads configuration data from the storage. Overrides StorageInterface::read
RecipeMultipleModulesConfigStorage::readMultiple public function Reads configuration data from the storage. Overrides StorageInterface::readMultiple
RecipeMultipleModulesConfigStorage::rename public function Renames a configuration object in the storage. Overrides StorageInterface::rename
RecipeMultipleModulesConfigStorage::write public function Writes configuration data to the storage. Overrides StorageInterface::write
RecipeMultipleModulesConfigStorage::__construct private function Constructs a RecipeMultipleModulesConfigStorage.
StorageInterface::DEFAULT_COLLECTION constant The default collection name.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.