de77e09d20f3ac94492900e3a65516592e1fdb1b
[yaffs-website] / web / core / lib / Drupal / Core / Entity / KeyValueStore / KeyValueEntityStorage.php
1 <?php
2
3 namespace Drupal\Core\Entity\KeyValueStore;
4
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;
15
16 /**
17  * Provides a key value backend for entities.
18  *
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.
23  */
24 class KeyValueEntityStorage extends EntityStorageBase {
25
26   /**
27    * Length limit of the entity ID.
28    */
29   const MAX_ID_LENGTH = 128;
30
31   /**
32    * The key value store.
33    *
34    * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
35    */
36   protected $keyValueStore;
37
38   /**
39    * The UUID service.
40    *
41    * @var \Drupal\Component\Uuid\UuidInterface
42    */
43   protected $uuidService;
44
45   /**
46    * The language manager.
47    *
48    * @var \Drupal\Core\Language\LanguageManagerInterface
49    */
50   protected $languageManager;
51
52   /**
53    * Constructs a new KeyValueEntityStorage.
54    *
55    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
56    *   The entity type.
57    * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $key_value_store
58    *   The key value store.
59    * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
60    *   The UUID service.
61    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
62    *   The language manager.
63    */
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;
69
70     // Check if the entity type supports UUIDs.
71     $this->uuidKey = $this->entityType->getKey('uuid');
72   }
73
74   /**
75    * {@inheritdoc}
76    */
77   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
78     return new static(
79       $entity_type,
80       $container->get('keyvalue')->get('entity_storage__' . $entity_type->id()),
81       $container->get('uuid'),
82       $container->get('language_manager')
83     );
84   }
85
86   /**
87    * {@inheritdoc}
88    */
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);
93
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
97     //   completely.
98     if ($entity instanceof FieldableEntityInterface) {
99       foreach ($entity as $name => $field) {
100         if (isset($values[$name])) {
101           $entity->$name = $values[$name];
102         }
103         elseif (!array_key_exists($name, $values)) {
104           $entity->get($name)->applyDefaultValue();
105         }
106         unset($values[$name]);
107       }
108     }
109
110     return $entity;
111   }
112
113   /**
114    * {@inheritdoc}
115    */
116   public function doLoadMultiple(array $ids = NULL) {
117     if (empty($ids)) {
118       $entities = $this->keyValueStore->getAll();
119     }
120     else {
121       $entities = $this->keyValueStore->getMultiple($ids);
122     }
123     return $this->mapFromStorageRecords($entities);
124   }
125
126   /**
127    * {@inheritdoc}
128    */
129   public function loadRevision($revision_id) {
130     return NULL;
131   }
132
133   /**
134    * {@inheritdoc}
135    */
136   public function deleteRevision($revision_id) {
137     return NULL;
138   }
139
140   /**
141    * {@inheritdoc}
142    */
143   public function doDelete($entities) {
144     $entity_ids = array_keys($entities);
145     $this->keyValueStore->deleteMultiple($entity_ids);
146   }
147
148   /**
149    * {@inheritdoc}
150    */
151   public function save(EntityInterface $entity) {
152     $id = $entity->id();
153     if ($id === NULL || $id === '') {
154       throw new EntityMalformedException('The entity does not have an ID.');
155     }
156
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.');
162     }
163     return parent::save($entity);
164   }
165
166   /**
167    * {@inheritdoc}
168    */
169   protected function doSave($id, EntityInterface $entity) {
170     $is_new = $entity->isNew();
171
172     // Save the entity data in the key value store.
173     $this->keyValueStore->set($entity->id(), $entity->toArray());
174
175     // If this is a rename, delete the original entity.
176     if ($this->has($id, $entity) && $id !== $entity->id()) {
177       $this->keyValueStore->delete($id);
178     }
179
180     return $is_new ? SAVED_NEW : SAVED_UPDATED;
181   }
182
183   /**
184    * {@inheritdoc}
185    */
186   protected function has($id, EntityInterface $entity) {
187     return $this->keyValueStore->has($id);
188   }
189
190   /**
191    * {@inheritdoc}
192    */
193   protected function getQueryServiceName() {
194     return 'entity.query.keyvalue';
195   }
196
197 }