3 namespace Drupal\Core\Entity\KeyValueStore;
5 use Drupal\Component\Uuid\UuidInterface;
6 use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
7 use Drupal\Core\Entity\FieldableEntityInterface;
8 use Drupal\Core\Entity\EntityInterface;
9 use Drupal\Core\Entity\EntityMalformedException;
10 use Drupal\Core\Entity\EntityStorageBase;
11 use Drupal\Core\Entity\EntityTypeInterface;
12 use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
13 use Drupal\Core\Language\LanguageManagerInterface;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
17 * Provides a key value backend for entities.
19 * @todo Entities that depend on auto-incrementing serial IDs need to explicitly
20 * provide an ID until a generic wrapper around the functionality provided by
21 * \Drupal\Core\Database\Connection::nextId() is added and used.
22 * @todo Revisions are currently not supported.
24 class KeyValueEntityStorage extends EntityStorageBase {
27 * Length limit of the entity ID.
29 const MAX_ID_LENGTH = 128;
32 * The key value store.
34 * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
36 protected $keyValueStore;
41 * @var \Drupal\Component\Uuid\UuidInterface
43 protected $uuidService;
46 * The language manager.
48 * @var \Drupal\Core\Language\LanguageManagerInterface
50 protected $languageManager;
53 * Constructs a new KeyValueEntityStorage.
55 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
57 * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value_store
58 * The key value store.
59 * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
61 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
62 * The language manager.
64 public function __construct(EntityTypeInterface $entity_type, KeyValueStoreInterface $key_value_store, UuidInterface $uuid_service, LanguageManagerInterface $language_manager) {
65 parent::__construct($entity_type);
66 $this->keyValueStore = $key_value_store;
67 $this->uuidService = $uuid_service;
68 $this->languageManager = $language_manager;
70 // Check if the entity type supports UUIDs.
71 $this->uuidKey = $this->entityType->getKey('uuid');
77 public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
80 $container->get('keyvalue')->get('entity_storage__' . $entity_type->id()),
81 $container->get('uuid'),
82 $container->get('language_manager')
89 public function doCreate(array $values = []) {
90 // Set default language to site default if not provided.
91 $values += [$this->getEntityType()->getKey('langcode') => $this->languageManager->getDefaultLanguage()->getId()];
92 $entity = new $this->entityClass($values, $this->entityTypeId);
94 // @todo This is handled by ContentEntityStorageBase, which assumes
95 // FieldableEntityInterface. The current approach in
96 // https://www.drupal.org/node/1867228 improves this but does not solve it
98 if ($entity instanceof FieldableEntityInterface) {
99 foreach ($entity as $name => $field) {
100 if (isset($values[$name])) {
101 $entity->$name = $values[$name];
103 elseif (!array_key_exists($name, $values)) {
104 $entity->get($name)->applyDefaultValue();
106 unset($values[$name]);
116 public function doLoadMultiple(array $ids = NULL) {
118 $entities = $this->keyValueStore->getAll();
121 $entities = $this->keyValueStore->getMultiple($ids);
123 return $this->mapFromStorageRecords($entities);
129 public function loadRevision($revision_id) {
136 public function deleteRevision($revision_id) {
143 public function doDelete($entities) {
144 $entity_ids = array_keys($entities);
145 $this->keyValueStore->deleteMultiple($entity_ids);
151 public function save(EntityInterface $entity) {
153 if ($id === NULL || $id === '') {
154 throw new EntityMalformedException('The entity does not have an ID.');
157 // Check the entity ID length.
158 // @todo This is not config-specific, but serial IDs will likely never hit
159 // this limit. Consider renaming the exception class.
160 if (strlen($entity->id()) > static::MAX_ID_LENGTH) {
161 throw new ConfigEntityIdLengthException("Entity ID {$entity->id()} exceeds maximum allowed length of " . static::MAX_ID_LENGTH . ' characters.');
163 return parent::save($entity);
169 protected function doSave($id, EntityInterface $entity) {
170 $is_new = $entity->isNew();
172 // Save the entity data in the key value store.
173 $this->keyValueStore->set($entity->id(), $entity->toArray());
175 // If this is a rename, delete the original entity.
176 if ($this->has($id, $entity) && $id !== $entity->id()) {
177 $this->keyValueStore->delete($id);
180 return $is_new ? SAVED_NEW : SAVED_UPDATED;
186 protected function has($id, EntityInterface $entity) {
187 return $this->keyValueStore->has($id);
193 protected function getQueryServiceName() {
194 return 'entity.query.keyvalue';