comparison core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
1 <?php
2
3 namespace Drupal\Core\Entity;
4
5 use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
6 use Drupal\Core\Entity\Schema\EntityStorageSchemaInterface;
7 use Drupal\Core\Field\BaseFieldDefinition;
8 use Drupal\Core\Field\FieldStorageDefinitionInterface;
9 use Drupal\Core\StringTranslation\StringTranslationTrait;
10
11 /**
12 * Manages entity definition updates.
13 */
14 class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInterface {
15 use StringTranslationTrait;
16
17 /**
18 * The entity manager service.
19 *
20 * @var \Drupal\Core\Entity\EntityManagerInterface
21 */
22 protected $entityManager;
23
24 /**
25 * Constructs a new EntityDefinitionUpdateManager.
26 *
27 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
28 * The entity manager.
29 */
30 public function __construct(EntityManagerInterface $entity_manager) {
31 $this->entityManager = $entity_manager;
32 }
33
34 /**
35 * {@inheritdoc}
36 */
37 public function needsUpdates() {
38 return (bool) $this->getChangeList();
39 }
40
41 /**
42 * {@inheritdoc}
43 */
44 public function getChangeSummary() {
45 $summary = [];
46
47 foreach ($this->getChangeList() as $entity_type_id => $change_list) {
48 // Process entity type definition changes.
49 if (!empty($change_list['entity_type'])) {
50 $entity_type = $this->entityManager->getDefinition($entity_type_id);
51
52 switch ($change_list['entity_type']) {
53 case static::DEFINITION_CREATED:
54 $summary[$entity_type_id][] = $this->t('The %entity_type entity type needs to be installed.', ['%entity_type' => $entity_type->getLabel()]);
55 break;
56
57 case static::DEFINITION_UPDATED:
58 $summary[$entity_type_id][] = $this->t('The %entity_type entity type needs to be updated.', ['%entity_type' => $entity_type->getLabel()]);
59 break;
60 }
61 }
62
63 // Process field storage definition changes.
64 if (!empty($change_list['field_storage_definitions'])) {
65 $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
66 $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
67
68 foreach ($change_list['field_storage_definitions'] as $field_name => $change) {
69 switch ($change) {
70 case static::DEFINITION_CREATED:
71 $summary[$entity_type_id][] = $this->t('The %field_name field needs to be installed.', ['%field_name' => $storage_definitions[$field_name]->getLabel()]);
72 break;
73
74 case static::DEFINITION_UPDATED:
75 $summary[$entity_type_id][] = $this->t('The %field_name field needs to be updated.', ['%field_name' => $storage_definitions[$field_name]->getLabel()]);
76 break;
77
78 case static::DEFINITION_DELETED:
79 $summary[$entity_type_id][] = $this->t('The %field_name field needs to be uninstalled.', ['%field_name' => $original_storage_definitions[$field_name]->getLabel()]);
80 break;
81 }
82 }
83 }
84 }
85
86 return $summary;
87 }
88
89 /**
90 * {@inheritdoc}
91 */
92 public function applyUpdates() {
93 $complete_change_list = $this->getChangeList();
94 if ($complete_change_list) {
95 // self::getChangeList() only disables the cache and does not invalidate.
96 // In case there are changes, explicitly invalidate caches.
97 $this->entityManager->clearCachedDefinitions();
98 }
99 foreach ($complete_change_list as $entity_type_id => $change_list) {
100 // Process entity type definition changes before storage definitions ones
101 // this is necessary when you change an entity type from non-revisionable
102 // to revisionable and at the same time add revisionable fields to the
103 // entity type.
104 if (!empty($change_list['entity_type'])) {
105 $this->doEntityUpdate($change_list['entity_type'], $entity_type_id);
106 }
107
108 // Process field storage definition changes.
109 if (!empty($change_list['field_storage_definitions'])) {
110 $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
111 $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
112
113 foreach ($change_list['field_storage_definitions'] as $field_name => $change) {
114 $storage_definition = isset($storage_definitions[$field_name]) ? $storage_definitions[$field_name] : NULL;
115 $original_storage_definition = isset($original_storage_definitions[$field_name]) ? $original_storage_definitions[$field_name] : NULL;
116 $this->doFieldUpdate($change, $storage_definition, $original_storage_definition);
117 }
118 }
119 }
120 }
121
122 /**
123 * {@inheritdoc}
124 */
125 public function getEntityType($entity_type_id) {
126 $entity_type = $this->entityManager->getLastInstalledDefinition($entity_type_id);
127 return $entity_type ? clone $entity_type : NULL;
128 }
129
130 /**
131 * {@inheritdoc}
132 */
133 public function installEntityType(EntityTypeInterface $entity_type) {
134 $this->entityManager->clearCachedDefinitions();
135 $this->entityManager->onEntityTypeCreate($entity_type);
136 }
137
138 /**
139 * {@inheritdoc}
140 */
141 public function updateEntityType(EntityTypeInterface $entity_type) {
142 $original = $this->getEntityType($entity_type->id());
143 $this->entityManager->clearCachedDefinitions();
144 $this->entityManager->onEntityTypeUpdate($entity_type, $original);
145 }
146
147 /**
148 * {@inheritdoc}
149 */
150 public function uninstallEntityType(EntityTypeInterface $entity_type) {
151 $this->entityManager->clearCachedDefinitions();
152 $this->entityManager->onEntityTypeDelete($entity_type);
153 }
154
155 /**
156 * {@inheritdoc}
157 */
158 public function installFieldStorageDefinition($name, $entity_type_id, $provider, FieldStorageDefinitionInterface $storage_definition) {
159 // @todo Pass a mutable field definition interface when we have one. See
160 // https://www.drupal.org/node/2346329.
161 if ($storage_definition instanceof BaseFieldDefinition) {
162 $storage_definition
163 ->setName($name)
164 ->setTargetEntityTypeId($entity_type_id)
165 ->setProvider($provider)
166 ->setTargetBundle(NULL);
167 }
168 $this->entityManager->clearCachedDefinitions();
169 $this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
170 }
171
172 /**
173 * {@inheritdoc}
174 */
175 public function getFieldStorageDefinition($name, $entity_type_id) {
176 $storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
177 return isset($storage_definitions[$name]) ? clone $storage_definitions[$name] : NULL;
178 }
179
180 /**
181 * {@inheritdoc}
182 */
183 public function updateFieldStorageDefinition(FieldStorageDefinitionInterface $storage_definition) {
184 $original = $this->getFieldStorageDefinition($storage_definition->getName(), $storage_definition->getTargetEntityTypeId());
185 $this->entityManager->clearCachedDefinitions();
186 $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $original);
187 }
188
189 /**
190 * {@inheritdoc}
191 */
192 public function uninstallFieldStorageDefinition(FieldStorageDefinitionInterface $storage_definition) {
193 $this->entityManager->clearCachedDefinitions();
194 $this->entityManager->onFieldStorageDefinitionDelete($storage_definition);
195 }
196
197 /**
198 * Performs an entity type definition update.
199 *
200 * @param string $op
201 * The operation to perform, either static::DEFINITION_CREATED or
202 * static::DEFINITION_UPDATED.
203 * @param string $entity_type_id
204 * The entity type ID.
205 */
206 protected function doEntityUpdate($op, $entity_type_id) {
207 $entity_type = $this->entityManager->getDefinition($entity_type_id);
208 switch ($op) {
209 case static::DEFINITION_CREATED:
210 $this->entityManager->onEntityTypeCreate($entity_type);
211 break;
212
213 case static::DEFINITION_UPDATED:
214 $original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
215 $this->entityManager->onEntityTypeUpdate($entity_type, $original);
216 break;
217 }
218 }
219
220 /**
221 * Performs a field storage definition update.
222 *
223 * @param string $op
224 * The operation to perform, possible values are static::DEFINITION_CREATED,
225 * static::DEFINITION_UPDATED or static::DEFINITION_DELETED.
226 * @param array|null $storage_definition
227 * The new field storage definition.
228 * @param array|null $original_storage_definition
229 * The original field storage definition.
230 */
231 protected function doFieldUpdate($op, $storage_definition = NULL, $original_storage_definition = NULL) {
232 switch ($op) {
233 case static::DEFINITION_CREATED:
234 $this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
235 break;
236
237 case static::DEFINITION_UPDATED:
238 $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $original_storage_definition);
239 break;
240
241 case static::DEFINITION_DELETED:
242 $this->entityManager->onFieldStorageDefinitionDelete($original_storage_definition);
243 break;
244 }
245 }
246
247 /**
248 * Gets a list of changes to entity type and field storage definitions.
249 *
250 * @return array
251 * An associative array keyed by entity type id of change descriptors. Every
252 * entry is an associative array with the following optional keys:
253 * - entity_type: a scalar having only the DEFINITION_UPDATED value.
254 * - field_storage_definitions: an associative array keyed by field name of
255 * scalars having one value among:
256 * - DEFINITION_CREATED
257 * - DEFINITION_UPDATED
258 * - DEFINITION_DELETED
259 */
260 protected function getChangeList() {
261 $this->entityManager->useCaches(FALSE);
262 $change_list = [];
263
264 foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
265 $original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
266
267 // @todo Support non-storage-schema-changing definition updates too:
268 // https://www.drupal.org/node/2336895.
269 if (!$original) {
270 $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_CREATED;
271 }
272 else {
273 if ($this->requiresEntityStorageSchemaChanges($entity_type, $original)) {
274 $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED;
275 }
276
277 if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) {
278 $field_changes = [];
279 $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
280 $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
281
282 // Detect created field storage definitions.
283 foreach (array_diff_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
284 $field_changes[$field_name] = static::DEFINITION_CREATED;
285 }
286
287 // Detect deleted field storage definitions.
288 foreach (array_diff_key($original_storage_definitions, $storage_definitions) as $field_name => $original_storage_definition) {
289 $field_changes[$field_name] = static::DEFINITION_DELETED;
290 }
291
292 // Detect updated field storage definitions.
293 foreach (array_intersect_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
294 // @todo Support non-storage-schema-changing definition updates too:
295 // https://www.drupal.org/node/2336895. So long as we're checking
296 // based on schema change requirements rather than definition
297 // equality, skip the check if the entity type itself needs to be
298 // updated, since that can affect the schema of all fields, so we
299 // want to process that update first without reporting false
300 // positives here.
301 if (!isset($change_list[$entity_type_id]['entity_type']) && $this->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) {
302 $field_changes[$field_name] = static::DEFINITION_UPDATED;
303 }
304 }
305
306 if ($field_changes) {
307 $change_list[$entity_type_id]['field_storage_definitions'] = $field_changes;
308 }
309 }
310 }
311 }
312
313 // @todo Support deleting entity definitions when we support base field
314 // purging. See https://www.drupal.org/node/2282119.
315
316 $this->entityManager->useCaches(TRUE);
317
318 return array_filter($change_list);
319 }
320
321 /**
322 * Checks if the changes to the entity type requires storage schema changes.
323 *
324 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
325 * The updated entity type definition.
326 * @param \Drupal\Core\Entity\EntityTypeInterface $original
327 * The original entity type definition.
328 *
329 * @return bool
330 * TRUE if storage schema changes are required, FALSE otherwise.
331 */
332 protected function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
333 $storage = $this->entityManager->getStorage($entity_type->id());
334 return ($storage instanceof EntityStorageSchemaInterface) && $storage->requiresEntityStorageSchemaChanges($entity_type, $original);
335 }
336
337 /**
338 * Checks if the changes to the storage definition requires schema changes.
339 *
340 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
341 * The updated field storage definition.
342 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
343 * The original field storage definition.
344 *
345 * @return bool
346 * TRUE if storage schema changes are required, FALSE otherwise.
347 */
348 protected function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
349 $storage = $this->entityManager->getStorage($storage_definition->getTargetEntityTypeId());
350 return ($storage instanceof DynamicallyFieldableEntityStorageSchemaInterface) && $storage->requiresFieldStorageSchemaChanges($storage_definition, $original);
351 }
352
353 }