Mercurial > hg > isophonics-drupal-site
comparison core/modules/field/field.purge.inc @ 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 /** | |
4 * @file | |
5 * Provides support for field data purge after mass deletion. | |
6 */ | |
7 | |
8 use Drupal\Core\Field\FieldException; | |
9 use Drupal\field\Entity\FieldStorageConfig; | |
10 use Drupal\field\FieldStorageConfigInterface; | |
11 use Drupal\field\FieldConfigInterface; | |
12 | |
13 /** | |
14 * @defgroup field_purge Field API bulk data deletion | |
15 * @{ | |
16 * Cleans up after Field API bulk deletion operations. | |
17 * | |
18 * Field API provides functions for deleting data attached to individual | |
19 * entities as well as deleting entire fields or field storages in a single | |
20 * operation. | |
21 * | |
22 * When a single entity is deleted, the Entity storage performs the | |
23 * following operations: | |
24 * - Invoking the method \Drupal\Core\Field\FieldItemListInterface::delete() for | |
25 * each field on the entity. A file field type might use this method to delete | |
26 * uploaded files from the filesystem. | |
27 * - Removing the data from storage. | |
28 * - Invoking the global hook_entity_delete() for all modules that implement it. | |
29 * Each hook implementation receives the entity being deleted and can operate | |
30 * on whichever subset of the entity's bundle's fields it chooses to. | |
31 * | |
32 * Similar operations are performed on deletion of a single entity revision. | |
33 * | |
34 * When a bundle, field or field storage is deleted, it is not practical to | |
35 * perform those operations immediately on every affected entity in a single | |
36 * page request; there could be thousands or millions of them. Instead, the | |
37 * appropriate field data items, fields, and/or field storages are marked as | |
38 * deleted so that subsequent load or query operations will not return them. | |
39 * Later, a separate process cleans up, or "purges", the marked-as-deleted data | |
40 * by going through the three-step process described above and, finally, | |
41 * removing deleted field storage and field records. | |
42 * | |
43 * Purging field data is made somewhat tricky by the fact that, while | |
44 * $entity->delete() has a complete entity to pass to the various deletion | |
45 * steps, the Field API purge process only has the field data it has previously | |
46 * stored. It cannot reconstruct complete original entities to pass to the | |
47 * deletion operations. It is even possible that the original entity to which | |
48 * some Field API data was attached has been itself deleted before the field | |
49 * purge operation takes place. | |
50 * | |
51 * Field API resolves this problem by using stub entities during purge | |
52 * operations, containing only the information from the original entity that | |
53 * Field API knows about: entity type, ID, revision ID, and bundle. It also | |
54 * contains the field data for whichever field is currently being purged. | |
55 * | |
56 * See @link field Field API @endlink for information about the other parts of | |
57 * the Field API. | |
58 */ | |
59 | |
60 /** | |
61 * Purges a batch of deleted Field API data, field storages, or fields. | |
62 * | |
63 * This function will purge deleted field data in batches. The batch size | |
64 * is defined as an argument to the function, and once each batch is finished, | |
65 * it continues with the next batch until all have completed. If a deleted field | |
66 * with no remaining data records is found, the field itself will | |
67 * be purged. If a deleted field storage with no remaining fields is found, the | |
68 * field storage itself will be purged. | |
69 * | |
70 * @param $batch_size | |
71 * The maximum number of field data records to purge before returning. | |
72 * @param string $field_storage_uuid | |
73 * (optional) Limit the purge to a specific field storage. | |
74 */ | |
75 function field_purge_batch($batch_size, $field_storage_uuid = NULL) { | |
76 $properties = [ | |
77 'deleted' => TRUE, | |
78 'include_deleted' => TRUE, | |
79 ]; | |
80 if ($field_storage_uuid) { | |
81 $properties['field_storage_uuid'] = $field_storage_uuid; | |
82 } | |
83 $fields = entity_load_multiple_by_properties('field_config', $properties); | |
84 | |
85 $info = \Drupal::entityManager()->getDefinitions(); | |
86 foreach ($fields as $field) { | |
87 $entity_type = $field->getTargetEntityTypeId(); | |
88 | |
89 // We cannot purge anything if the entity type is unknown (e.g. the | |
90 // providing module was uninstalled). | |
91 // @todo Revisit after https://www.drupal.org/node/2080823. | |
92 if (!isset($info[$entity_type])) { | |
93 continue; | |
94 } | |
95 | |
96 $count_purged = \Drupal::entityManager()->getStorage($entity_type)->purgeFieldData($field, $batch_size); | |
97 if ($count_purged < $batch_size || $count_purged == 0) { | |
98 // No field data remains for the field, so we can remove it. | |
99 field_purge_field($field); | |
100 } | |
101 $batch_size -= $count_purged; | |
102 // Only delete up to the maximum number of records. | |
103 if ($batch_size == 0) { | |
104 break; | |
105 } | |
106 } | |
107 | |
108 // Retrieve all deleted field storages. Any that have no fields can be purged. | |
109 $deleted_storages = \Drupal::state()->get('field.storage.deleted') ?: []; | |
110 foreach ($deleted_storages as $field_storage) { | |
111 $field_storage = new FieldStorageConfig($field_storage); | |
112 if ($field_storage_uuid && $field_storage->uuid() != $field_storage_uuid) { | |
113 // If a specific UUID is provided, only purge the corresponding field. | |
114 continue; | |
115 } | |
116 | |
117 // We cannot purge anything if the entity type is unknown (e.g. the | |
118 // providing module was uninstalled). | |
119 // @todo Revisit after https://www.drupal.org/node/2080823. | |
120 if (!isset($info[$field_storage->getTargetEntityTypeId()])) { | |
121 continue; | |
122 } | |
123 | |
124 $fields = entity_load_multiple_by_properties('field_config', ['field_storage_uuid' => $field_storage->uuid(), 'include_deleted' => TRUE]); | |
125 if (empty($fields)) { | |
126 field_purge_field_storage($field_storage); | |
127 } | |
128 } | |
129 } | |
130 | |
131 /** | |
132 * Purges a field record from the database. | |
133 * | |
134 * This function assumes all data for the field has already been purged and | |
135 * should only be called by field_purge_batch(). | |
136 * | |
137 * @param $field | |
138 * The field record to purge. | |
139 */ | |
140 function field_purge_field(FieldConfigInterface $field) { | |
141 $state = \Drupal::state(); | |
142 $deleted_fields = $state->get('field.field.deleted'); | |
143 unset($deleted_fields[$field->uuid()]); | |
144 $state->set('field.field.deleted', $deleted_fields); | |
145 | |
146 // Invoke external hooks after the cache is cleared for API consistency. | |
147 \Drupal::moduleHandler()->invokeAll('field_purge_field', [$field]); | |
148 } | |
149 | |
150 /** | |
151 * Purges a field record from the database. | |
152 * | |
153 * This function assumes all fields for the field storage has already been | |
154 * purged, and should only be called by field_purge_batch(). | |
155 * | |
156 * @param \Drupal\field\FieldStorageConfigInterface $field_storage | |
157 * The field storage to purge. | |
158 * | |
159 * @throws Drupal\field\FieldException | |
160 */ | |
161 function field_purge_field_storage(FieldStorageConfigInterface $field_storage) { | |
162 $fields = entity_load_multiple_by_properties('field_config', ['field_storage_uuid' => $field_storage->uuid(), 'include_deleted' => TRUE]); | |
163 if (count($fields) > 0) { | |
164 throw new FieldException(t('Attempt to purge a field storage @field_name that still has fields.', ['@field_name' => $field_storage->getName()])); | |
165 } | |
166 | |
167 $state = \Drupal::state(); | |
168 $deleted_storages = $state->get('field.storage.deleted'); | |
169 unset($deleted_storages[$field_storage->uuid()]); | |
170 $state->set('field.storage.deleted', $deleted_storages); | |
171 | |
172 // Notify the storage layer. | |
173 \Drupal::entityManager()->getStorage($field_storage->getTargetEntityTypeId())->finalizePurge($field_storage); | |
174 | |
175 // Invoke external hooks after the cache is cleared for API consistency. | |
176 \Drupal::moduleHandler()->invokeAll('field_purge_field_storage', [$field_storage]); | |
177 } | |
178 | |
179 /** | |
180 * @} End of "defgroup field_purge". | |
181 */ |