class ConfigEntityStorage

Same name in other branches
  1. 9 core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php \Drupal\Core\Config\Entity\ConfigEntityStorage
  2. 8.9.x core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php \Drupal\Core\Config\Entity\ConfigEntityStorage
  3. 11.x core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php \Drupal\Core\Config\Entity\ConfigEntityStorage

Defines the storage class for configuration entities.

Configuration object names of configuration entities are comprised of two parts, separated by a dot:

  • config_prefix: A string denoting the owner (module/extension) of the configuration object, followed by arbitrary other namespace identifiers that are declared by the owning extension; e.g., 'node.type'. The config_prefix does NOT contain a trailing dot. It is defined by the entity type's annotation.
  • ID: A string denoting the entity ID within the entity type namespace; e.g., 'article'. Entity IDs may contain dots/periods. The entire remaining string after the config_prefix in a config name forms the entity ID. Additional or custom suffixes are not possible.

Hierarchy

Expanded class hierarchy of ConfigEntityStorage

Related topics

16 files declare their use of ConfigEntityStorage
BlockStorageUnitTest.php in core/modules/block/tests/src/Kernel/BlockStorageUnitTest.php
BundleConfigImportValidate.php in core/lib/Drupal/Core/Entity/Event/BundleConfigImportValidate.php
ConfigDependencyWebTest.php in core/modules/config/tests/src/Functional/ConfigDependencyWebTest.php
ConfigEntityStorageTest.php in core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
ConfigEntityTest.php in core/modules/config/tests/src/Functional/ConfigEntityTest.php

... See full list

File

core/lib/Drupal/Core/Config/Entity/ConfigEntityStorage.php, line 36

Namespace

Drupal\Core\Config\Entity
View source
class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStorageInterface, ImportableEntityStorageInterface {
    
    /**
     * Length limit of the configuration entity ID.
     *
     * Most file systems limit a file name's length to 255 characters, so
     * ConfigBase::MAX_NAME_LENGTH restricts the full configuration object name
     * to 250 characters (leaving 5 for the file extension). The config prefix
     * is limited by ConfigEntityType::PREFIX_LENGTH to 83 characters, so this
     * leaves 166 remaining characters for the configuration entity ID, with 1
     * additional character needed for the joining dot.
     *
     * @see \Drupal\Core\Config\ConfigBase::MAX_NAME_LENGTH
     * @see \Drupal\Core\Config\Entity\ConfigEntityType::PREFIX_LENGTH
     */
    const MAX_ID_LENGTH = 166;
    
    /**
     * {@inheritdoc}
     */
    protected $uuidKey = 'uuid';
    
    /**
     * The config factory service.
     *
     * @var \Drupal\Core\Config\ConfigFactoryInterface
     */
    protected $configFactory;
    
    /**
     * The config storage service.
     *
     * @var \Drupal\Core\Config\StorageInterface
     */
    protected $configStorage;
    
    /**
     * The language manager.
     *
     * @var \Drupal\Core\Language\LanguageManagerInterface
     */
    protected $languageManager;
    
    /**
     * Determines if the underlying configuration is retrieved override free.
     *
     * @var bool
     */
    protected $overrideFree = FALSE;
    
    /**
     * Constructs a ConfigEntityStorage object.
     *
     * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
     *   The entity type definition.
     * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
     *   The config factory service.
     * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
     *   The UUID service.
     * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
     *   The language manager.
     * @param \Drupal\Core\Cache\MemoryCache\MemoryCacheInterface $memory_cache
     *   The memory cache backend.
     */
    public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, UuidInterface $uuid_service, LanguageManagerInterface $language_manager, MemoryCacheInterface $memory_cache) {
        parent::__construct($entity_type, $memory_cache);
        $this->configFactory = $config_factory;
        $this->uuidService = $uuid_service;
        $this->languageManager = $language_manager;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
        return new static($entity_type, $container->get('config.factory'), $container->get('uuid'), $container->get('language_manager'), $container->get('entity.memory_cache'));
    }
    
    /**
     * {@inheritdoc}
     */
    public function loadRevision($revision_id) {
        @trigger_error(__METHOD__ . '() is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use \\Drupal\\Core\\Entity\\RevisionableStorageInterface::loadRevision instead. See https://www.drupal.org/node/3294237', E_USER_DEPRECATED);
        return NULL;
    }
    
    /**
     * {@inheritdoc}
     */
    public function deleteRevision($revision_id) {
        @trigger_error(__METHOD__ . '() is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. Use \\Drupal\\Core\\Entity\\RevisionableStorageInterface::deleteRevision instead. See https://www.drupal.org/node/3294237', E_USER_DEPRECATED);
        return NULL;
    }
    
    /**
     * Returns the prefix used to create the configuration name.
     *
     * The prefix consists of the config prefix from the entity type plus a dot
     * for separating from the ID.
     *
     * @return string
     *   The full configuration prefix, for example 'views.view.'.
     */
    protected function getPrefix() {
        return $this->entityType
            ->getConfigPrefix() . '.';
    }
    
    /**
     * {@inheritdoc}
     */
    public static function getIDFromConfigName($config_name, $config_prefix) {
        return substr($config_name, strlen($config_prefix . '.'));
    }
    
    /**
     * {@inheritdoc}
     */
    protected function doLoadMultiple(?array $ids = NULL) {
        $prefix = $this->getPrefix();
        // Get the names of the configuration entities we are going to load.
        if ($ids === NULL) {
            $names = $this->configFactory
                ->listAll($prefix);
        }
        else {
            $names = [];
            foreach ($ids as $id) {
                // Add the prefix to the ID to serve as the configuration object name.
                $names[] = $prefix . $id;
            }
        }
        // Load all of the configuration entities.
        
        /** @var \Drupal\Core\Config\Config[] $configs */
        $configs = [];
        $records = [];
        foreach ($this->configFactory
            ->loadMultiple($names) as $config) {
            $id = $config->get($this->idKey);
            $records[$id] = $this->overrideFree ? $config->getOriginal(NULL, FALSE) : $config->get();
            $configs[$id] = $config;
        }
        $entities = $this->mapFromStorageRecords($records);
        // Config entities wrap config objects, and therefore they need to inherit
        // the cacheability metadata of config objects (to ensure e.g. additional
        // cacheability metadata added by config overrides is not lost).
        foreach ($entities as $id => $entity) {
            // But rather than simply inheriting all cacheability metadata of config
            // objects, we need to make sure the self-referring cache tag that is
            // present on Config objects is not added to the Config entity. It must be
            // removed for 3 reasons:
            // 1. When renaming/duplicating a Config entity, the cache tag of the
            //    original config object would remain present, which would be wrong.
            // 2. Some Config entities choose to not use the cache tag that the under-
            //    lying Config object provides by default (For performance and
            //    cacheability reasons it may not make sense to have a unique cache
            //    tag for every Config entity. The DateFormat Config entity specifies
            //    the 'rendered' cache tag for example, because A) date formats are
            //    changed extremely rarely, so invalidating all render cache items is
            //    fine, B) it means fewer cache tags per page.).
            // 3. Fewer cache tags is better for performance.
            $self_referring_cache_tag = [
                'config:' . $configs[$id]->getName(),
            ];
            $config_cacheability = CacheableMetadata::createFromObject($configs[$id]);
            $config_cacheability->setCacheTags(array_diff($config_cacheability->getCacheTags(), $self_referring_cache_tag));
            $entity->addCacheableDependency($config_cacheability);
        }
        return $entities;
    }
    
    /**
     * {@inheritdoc}
     */
    protected function doCreate(array $values) {
        // Set default language to current language if not provided.
        $values += [
            $this->langcodeKey => $this->languageManager
                ->getCurrentLanguage()
                ->getId(),
        ];
        $entity_class = $this->getEntityClass();
        $entity = new $entity_class($values, $this->entityTypeId);
        return $entity;
    }
    
    /**
     * {@inheritdoc}
     */
    protected function doDelete($entities) {
        foreach ($entities as $entity) {
            $this->configFactory
                ->getEditable($this->getPrefix() . $entity->id())
                ->delete();
        }
    }
    
    /**
     * Implements Drupal\Core\Entity\EntityStorageInterface::save().
     *
     * @throws \Drupal\Core\Entity\EntityMalformedException
     *   When attempting to save a configuration entity that has no ID.
     */
    public function save(EntityInterface $entity) {
        // Configuration entity IDs are strings, and '0' is a valid ID.
        $id = $entity->id();
        if ($id === NULL || $id === '') {
            throw new EntityMalformedException('The entity does not have an ID.');
        }
        // Check the configuration entity ID length.
        // @see \Drupal\Core\Config\Entity\ConfigEntityStorage::MAX_ID_LENGTH
        // @todo Consider moving this to a protected method on the parent class, and
        //   abstracting it for all entity types.
        if (strlen($id) > static::MAX_ID_LENGTH) {
            throw new ConfigEntityIdLengthException("Configuration entity ID {$id} exceeds maximum allowed length of " . static::MAX_ID_LENGTH . " characters.");
        }
        return parent::save($entity);
    }
    
    /**
     * {@inheritdoc}
     */
    protected function doSave($id, EntityInterface $entity) {
        $is_new = $entity->isNew();
        $prefix = $this->getPrefix();
        $config_name = $prefix . $entity->id();
        if ($id !== $entity->id()) {
            // Renaming a config object needs to cater for:
            // - Storage needs to access the original object.
            // - The object needs to be renamed/copied in ConfigFactory and reloaded.
            // - All instances of the object need to be renamed.
            $this->configFactory
                ->rename($prefix . $id, $config_name);
        }
        $config = $this->configFactory
            ->getEditable($config_name);
        // Retrieve the desired properties and set them in config.
        $config->setData($this->mapToStorageRecord($entity));
        $config->save($entity->hasTrustedData());
        // Update the entity with the values stored in configuration. It is possible
        // that configuration schema has casted some of the values.
        if (!$entity->hasTrustedData()) {
            $data = $this->mapFromStorageRecords([
                $config->get(),
            ]);
            $updated_entity = current($data);
            foreach (array_keys($config->get()) as $property) {
                $value = $updated_entity->get($property);
                $entity->set($property, $value);
            }
        }
        return $is_new ? SAVED_NEW : SAVED_UPDATED;
    }
    
    /**
     * Maps from an entity object to the storage record.
     *
     * @param \Drupal\Core\Entity\EntityInterface $entity
     *   The entity object.
     *
     * @return array
     *   The record to store.
     */
    protected function mapToStorageRecord(EntityInterface $entity) {
        return $entity->toArray();
    }
    
    /**
     * {@inheritdoc}
     */
    protected function has($id, EntityInterface $entity) {
        $prefix = $this->getPrefix();
        $config = $this->configFactory
            ->get($prefix . $id);
        return !$config->isNew();
    }
    
    /**
     * {@inheritdoc}
     */
    public function hasData() {
        return (bool) $this->configFactory
            ->listAll($this->getPrefix());
    }
    
    /**
     * {@inheritdoc}
     */
    protected function buildCacheId($id) {
        return parent::buildCacheId($id) . ':' . ($this->overrideFree ? '' : implode(':', $this->configFactory
            ->getCacheKeys()));
    }
    
    /**
     * Invokes a hook on behalf of the entity.
     *
     * @param $hook
     *   One of 'presave', 'insert', 'update', 'predelete', or 'delete'.
     * @param $entity
     *   The entity object.
     */
    protected function invokeHook($hook, EntityInterface $entity) {
        // Invoke the hook.
        $this->moduleHandler
            ->invokeAll($this->entityTypeId . '_' . $hook, [
            $entity,
        ]);
        // Invoke the respective entity-level hook.
        $this->moduleHandler
            ->invokeAll('entity_' . $hook, [
            $entity,
            $this->entityTypeId,
        ]);
    }
    
    /**
     * {@inheritdoc}
     */
    protected function getQueryServiceName() {
        return 'entity.query.config';
    }
    
    /**
     * {@inheritdoc}
     */
    public function importCreate($name, Config $new_config, Config $old_config) {
        $entity = $this->_doCreateFromStorageRecord($new_config->get(), TRUE);
        $entity->save();
        return TRUE;
    }
    
    /**
     * {@inheritdoc}
     */
    public function importUpdate($name, Config $new_config, Config $old_config) {
        $id = static::getIDFromConfigName($name, $this->entityType
            ->getConfigPrefix());
        $entity = $this->load($id);
        if (!$entity) {
            throw new ConfigImporterException("Attempt to update non-existing entity '{$id}'.");
        }
        $entity->setSyncing(TRUE);
        $entity = $this->updateFromStorageRecord($entity, $new_config->get());
        $entity->save();
        return TRUE;
    }
    
    /**
     * {@inheritdoc}
     */
    public function importDelete($name, Config $new_config, Config $old_config) {
        $id = static::getIDFromConfigName($name, $this->entityType
            ->getConfigPrefix());
        $entity = $this->load($id);
        $entity->setSyncing(TRUE);
        $entity->delete();
        return TRUE;
    }
    
    /**
     * {@inheritdoc}
     */
    public function importRename($old_name, Config $new_config, Config $old_config) {
        return $this->importUpdate($old_name, $new_config, $old_config);
    }
    
    /**
     * {@inheritdoc}
     */
    public function createFromStorageRecord(array $values) {
        return $this->_doCreateFromStorageRecord($values);
    }
    
    /**
     * Helps create a configuration entity from storage values.
     *
     * Allows the configuration entity storage to massage storage values before
     * creating an entity.
     *
     * @param array $values
     *   The array of values from the configuration storage.
     * @param bool $is_syncing
     *   Is the configuration entity being created as part of a config sync.
     *
     * @return \Drupal\Core\Config\Entity\ConfigEntityInterface
     *   The configuration entity.
     *
     * @see \Drupal\Core\Config\Entity\ConfigEntityStorageInterface::createFromStorageRecord()
     * @see \Drupal\Core\Config\Entity\ImportableEntityStorageInterface::importCreate()
     */
    protected function _doCreateFromStorageRecord(array $values, $is_syncing = FALSE) {
        // Assign a new UUID if there is none yet.
        if ($this->uuidKey && $this->uuidService && !isset($values[$this->uuidKey])) {
            $values[$this->uuidKey] = $this->uuidService
                ->generate();
        }
        $data = $this->mapFromStorageRecords([
            $values,
        ]);
        
        /** @var \Drupal\Core\Config\Entity\ConfigEntityInterface $entity */
        $entity = current($data);
        $entity->original = clone $entity;
        $entity->setSyncing($is_syncing);
        $entity->enforceIsNew();
        $entity->postCreate($this);
        // Modules might need to add or change the data initially held by the new
        // entity object, for instance to fill-in default values.
        $this->invokeHook('create', $entity);
        return $entity;
    }
    
    /**
     * {@inheritdoc}
     */
    public function updateFromStorageRecord(ConfigEntityInterface $entity, array $values) {
        $entity->original = clone $entity;
        $data = $this->mapFromStorageRecords([
            $values,
        ]);
        $updated_entity = current($data);
        
        /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $entity_type */
        $entity_type = $this->getEntityType();
        $id_key = $entity_type->getKey('id');
        $properties = $entity_type->getPropertiesToExport($updated_entity->get($id_key));
        if (empty($properties)) {
            // Fallback to using the provided values. If the properties cannot be
            // determined for the config entity type annotation or configuration
            // schema.
            $properties = array_keys($values);
        }
        foreach ($properties as $property) {
            if ($property === $this->uuidKey) {
                // During an update the UUID field should not be copied. Under regular
                // circumstances the values will be equal. If configuration is written
                // twice during configuration install the updated entity will not have a
                // UUID.
                // @see \Drupal\Core\Config\ConfigInstaller::createConfiguration()
                continue;
            }
            $entity->set($property, $updated_entity->get($property));
        }
        return $entity;
    }
    
    /**
     * {@inheritdoc}
     */
    public function loadOverrideFree($id) {
        $entities = $this->loadMultipleOverrideFree([
            $id,
        ]);
        return $entities[$id] ?? NULL;
    }
    
    /**
     * {@inheritdoc}
     */
    public function loadMultipleOverrideFree(?array $ids = NULL) {
        $this->overrideFree = TRUE;
        $entities = $this->loadMultiple($ids);
        $this->overrideFree = FALSE;
        return $entities;
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
ConfigEntityStorage::$configFactory protected property The config factory service.
ConfigEntityStorage::$configStorage protected property The config storage service.
ConfigEntityStorage::$languageManager protected property The language manager.
ConfigEntityStorage::$overrideFree protected property Determines if the underlying configuration is retrieved override free.
ConfigEntityStorage::$uuidKey protected property Overrides EntityStorageBase::$uuidKey
ConfigEntityStorage::buildCacheId protected function Overrides EntityStorageBase::buildCacheId
ConfigEntityStorage::createFromStorageRecord public function Overrides ConfigEntityStorageInterface::createFromStorageRecord
ConfigEntityStorage::createInstance public static function Overrides EntityHandlerInterface::createInstance 4
ConfigEntityStorage::deleteRevision public function Overrides EntityStorageInterface::deleteRevision
ConfigEntityStorage::doCreate protected function Overrides EntityStorageBase::doCreate
ConfigEntityStorage::doDelete protected function Overrides EntityStorageBase::doDelete
ConfigEntityStorage::doLoadMultiple protected function Overrides EntityStorageBase::doLoadMultiple
ConfigEntityStorage::doSave protected function Overrides EntityStorageBase::doSave
ConfigEntityStorage::getIDFromConfigName public static function Overrides ConfigEntityStorageInterface::getIDFromConfigName
ConfigEntityStorage::getPrefix protected function Returns the prefix used to create the configuration name.
ConfigEntityStorage::getQueryServiceName protected function Overrides EntityStorageBase::getQueryServiceName
ConfigEntityStorage::has protected function Overrides EntityStorageBase::has
ConfigEntityStorage::hasData public function Overrides EntityStorageBase::hasData
ConfigEntityStorage::importCreate public function Overrides ImportableEntityStorageInterface::importCreate 1
ConfigEntityStorage::importDelete public function Overrides ImportableEntityStorageInterface::importDelete 2
ConfigEntityStorage::importRename public function Overrides ImportableEntityStorageInterface::importRename
ConfigEntityStorage::importUpdate public function Overrides ImportableEntityStorageInterface::importUpdate 1
ConfigEntityStorage::invokeHook protected function Invokes a hook on behalf of the entity. Overrides EntityStorageBase::invokeHook
ConfigEntityStorage::loadMultipleOverrideFree public function Overrides ConfigEntityStorageInterface::loadMultipleOverrideFree
ConfigEntityStorage::loadOverrideFree public function Overrides ConfigEntityStorageInterface::loadOverrideFree
ConfigEntityStorage::loadRevision public function Overrides EntityStorageInterface::loadRevision
ConfigEntityStorage::mapToStorageRecord protected function Maps from an entity object to the storage record. 3
ConfigEntityStorage::MAX_ID_LENGTH constant Length limit of the configuration entity ID. 1
ConfigEntityStorage::save public function Implements Drupal\Core\Entity\EntityStorageInterface::save(). Overrides EntityStorageBase::save
ConfigEntityStorage::updateFromStorageRecord public function Overrides ConfigEntityStorageInterface::updateFromStorageRecord
ConfigEntityStorage::_doCreateFromStorageRecord protected function Helps create a configuration entity from storage values.
ConfigEntityStorage::__construct public function Constructs a ConfigEntityStorage object. Overrides EntityStorageBase::__construct 4
DependencySerializationTrait::$_entityStorages protected property
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
EntityHandlerBase::$moduleHandler protected property The module handler to invoke hooks on. 5
EntityHandlerBase::moduleHandler protected function Gets the module handler. 5
EntityHandlerBase::setModuleHandler public function Sets the module handler for this handler.
EntityStorageBase::$baseEntityClass private property Name of the base entity class.
EntityStorageBase::$entityType protected property Information about the entity type.
EntityStorageBase::$entityTypeId protected property Entity type ID for this storage.
EntityStorageBase::$idKey protected property Name of the entity's ID field in the entity database table.
EntityStorageBase::$langcodeKey protected property The name of the entity langcode property. 1
EntityStorageBase::$memoryCache protected property The memory cache.
EntityStorageBase::$memoryCacheTag protected property The memory cache tag.
EntityStorageBase::$uuidService protected property The UUID service. 1
EntityStorageBase::buildPropertyQuery protected function Builds an entity query. 1
EntityStorageBase::create public function Overrides EntityStorageInterface::create 1
EntityStorageBase::delete public function Overrides EntityStorageInterface::delete 2
EntityStorageBase::doPostSave protected function Performs post save entity processing. 1
EntityStorageBase::doPreSave protected function Performs presave entity processing. 1
EntityStorageBase::getAggregateQuery public function Overrides EntityStorageInterface::getAggregateQuery
EntityStorageBase::getEntitiesByClass protected function Indexes the given array of entities by their class name and ID.
EntityStorageBase::getEntityClass public function Overrides EntityStorageInterface::getEntityClass 1
EntityStorageBase::getEntityType public function Overrides EntityStorageInterface::getEntityType
EntityStorageBase::getEntityTypeId public function Overrides EntityStorageInterface::getEntityTypeId
EntityStorageBase::getFromStaticCache protected function Gets entities from the static cache.
EntityStorageBase::getQuery public function Overrides EntityStorageInterface::getQuery
EntityStorageBase::load public function Overrides EntityStorageInterface::load 2
EntityStorageBase::loadByProperties public function Overrides EntityStorageInterface::loadByProperties 3
EntityStorageBase::loadMultiple public function Overrides EntityStorageInterface::loadMultiple 1
EntityStorageBase::loadUnchanged public function Overrides EntityStorageInterface::loadUnchanged 1
EntityStorageBase::mapFromStorageRecords protected function Maps from storage records to entity objects. 4
EntityStorageBase::postLoad protected function Attaches data to entities upon loading.
EntityStorageBase::preLoad protected function Gathers entities from a 'preload' step. 1
EntityStorageBase::resetCache public function Overrides EntityStorageInterface::resetCache 1
EntityStorageBase::restore public function Overrides EntityStorageInterface::restore 1
EntityStorageBase::setStaticCache protected function Stores entities in the static entity cache.
EntityStorageInterface::FIELD_LOAD_CURRENT constant Load the most recent version of an entity's field data.
EntityStorageInterface::FIELD_LOAD_REVISION constant Load the version of an entity's field data specified in the entity.
StringTranslationTrait::$stringTranslation protected property The string translation service. 3
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.

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