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