Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\field;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Core\Config\ConfigImporter;
|
Chris@0
|
6 use Drupal\Core\Config\Entity\ConfigEntityStorage;
|
Chris@0
|
7 use Drupal\field\Entity\FieldStorageConfig;
|
Chris@0
|
8
|
Chris@0
|
9 /**
|
Chris@0
|
10 * Processes field purges before a configuration synchronization.
|
Chris@0
|
11 */
|
Chris@0
|
12 class ConfigImporterFieldPurger {
|
Chris@0
|
13
|
Chris@0
|
14 /**
|
Chris@0
|
15 * Processes fields targeted for purge as part of a configuration sync.
|
Chris@0
|
16 *
|
Chris@0
|
17 * This takes care of deleting the field if necessary, and purging the data on
|
Chris@0
|
18 * the fly.
|
Chris@0
|
19 *
|
Chris@0
|
20 * @param array $context
|
Chris@0
|
21 * The batch context.
|
Chris@0
|
22 * @param \Drupal\Core\Config\ConfigImporter $config_importer
|
Chris@0
|
23 * The config importer.
|
Chris@0
|
24 */
|
Chris@0
|
25 public static function process(array &$context, ConfigImporter $config_importer) {
|
Chris@0
|
26 if (!isset($context['sandbox']['field'])) {
|
Chris@0
|
27 static::initializeSandbox($context, $config_importer);
|
Chris@0
|
28 }
|
Chris@0
|
29
|
Chris@0
|
30 // Get the list of field storages to purge.
|
Chris@0
|
31 $field_storages = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete'));
|
Chris@0
|
32 // Get the first field storage to process.
|
Chris@0
|
33 $field_storage = reset($field_storages);
|
Chris@0
|
34 if (!isset($context['sandbox']['field']['current_storage_id']) || $context['sandbox']['field']['current_storage_id'] != $field_storage->id()) {
|
Chris@0
|
35 $context['sandbox']['field']['current_storage_id'] = $field_storage->id();
|
Chris@0
|
36 // If the storage has not been deleted yet we need to do that. This is the
|
Chris@0
|
37 // case when the storage deletion is staged.
|
Chris@0
|
38 if (!$field_storage->isDeleted()) {
|
Chris@0
|
39 $field_storage->delete();
|
Chris@0
|
40 }
|
Chris@0
|
41 }
|
Chris@14
|
42 field_purge_batch($context['sandbox']['field']['purge_batch_size'], $field_storage->getUniqueStorageIdentifier());
|
Chris@0
|
43 $context['sandbox']['field']['current_progress']++;
|
Chris@0
|
44 $fields_to_delete_count = count(static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete')));
|
Chris@0
|
45 if ($fields_to_delete_count == 0) {
|
Chris@0
|
46 $context['finished'] = 1;
|
Chris@0
|
47 }
|
Chris@0
|
48 else {
|
Chris@0
|
49 $context['finished'] = $context['sandbox']['field']['current_progress'] / $context['sandbox']['field']['steps_to_delete'];
|
Chris@0
|
50 $context['message'] = \Drupal::translation()->translate('Purging field @field_label', ['@field_label' => $field_storage->label()]);
|
Chris@0
|
51 }
|
Chris@0
|
52 }
|
Chris@0
|
53
|
Chris@0
|
54 /**
|
Chris@0
|
55 * Initializes the batch context sandbox for processing field deletions.
|
Chris@0
|
56 *
|
Chris@0
|
57 * This calculates the number of steps necessary to purge all the field data
|
Chris@0
|
58 * and saves data for later use.
|
Chris@0
|
59 *
|
Chris@0
|
60 * @param array $context
|
Chris@0
|
61 * The batch context.
|
Chris@0
|
62 * @param \Drupal\Core\Config\ConfigImporter $config_importer
|
Chris@0
|
63 * The config importer.
|
Chris@0
|
64 */
|
Chris@0
|
65 protected static function initializeSandbox(array &$context, ConfigImporter $config_importer) {
|
Chris@0
|
66 $context['sandbox']['field']['purge_batch_size'] = \Drupal::config('field.settings')->get('purge_batch_size');
|
Chris@0
|
67 // Save the future list of installed extensions to limit the amount of times
|
Chris@0
|
68 // the configuration is read from disk.
|
Chris@0
|
69 $context['sandbox']['field']['extensions'] = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension');
|
Chris@0
|
70
|
Chris@0
|
71 $context['sandbox']['field']['steps_to_delete'] = 0;
|
Chris@0
|
72 $fields = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete'));
|
Chris@0
|
73 foreach ($fields as $field) {
|
Chris@0
|
74 $row_count = \Drupal::entityManager()->getStorage($field->getTargetEntityTypeId())
|
Chris@0
|
75 ->countFieldData($field);
|
Chris@0
|
76 if ($row_count > 0) {
|
Chris@0
|
77 // The number of steps to delete each field is determined by the
|
Chris@0
|
78 // purge_batch_size setting. For example if the field has 9 rows and the
|
Chris@0
|
79 // batch size is 10 then this will add 1 step to $number_of_steps.
|
Chris@0
|
80 $how_many_steps = ceil($row_count / $context['sandbox']['field']['purge_batch_size']);
|
Chris@0
|
81 $context['sandbox']['field']['steps_to_delete'] += $how_many_steps;
|
Chris@0
|
82 }
|
Chris@0
|
83 }
|
Chris@0
|
84 // Each field possibly needs one last field_purge_batch() call to remove the
|
Chris@0
|
85 // last field and the field storage itself.
|
Chris@0
|
86 $context['sandbox']['field']['steps_to_delete'] += count($fields);
|
Chris@0
|
87
|
Chris@0
|
88 $context['sandbox']['field']['current_progress'] = 0;
|
Chris@0
|
89 }
|
Chris@0
|
90
|
Chris@0
|
91 /**
|
Chris@0
|
92 * Gets the list of fields to purge before configuration synchronization.
|
Chris@0
|
93 *
|
Chris@0
|
94 * If, during a configuration synchronization, a field is being deleted and
|
Chris@0
|
95 * the module that provides the field type is being uninstalled then the field
|
Chris@0
|
96 * data must be purged before the module is uninstalled. Also, if deleted
|
Chris@0
|
97 * fields exist whose field types are provided by modules that are being
|
Chris@0
|
98 * uninstalled their data need to be purged too.
|
Chris@0
|
99 *
|
Chris@0
|
100 * @param array $extensions
|
Chris@0
|
101 * The list of extensions that will be enabled after the configuration
|
Chris@0
|
102 * synchronization has finished.
|
Chris@0
|
103 * @param array $deletes
|
Chris@0
|
104 * The configuration that will be deleted by the configuration
|
Chris@0
|
105 * synchronization.
|
Chris@0
|
106 *
|
Chris@0
|
107 * @return \Drupal\field\Entity\FieldStorageConfig[]
|
Chris@0
|
108 * An array of field storages that need purging before configuration can be
|
Chris@0
|
109 * synchronized.
|
Chris@0
|
110 */
|
Chris@0
|
111 public static function getFieldStoragesToPurge(array $extensions, array $deletes) {
|
Chris@0
|
112 $providers = array_keys($extensions['module']);
|
Chris@0
|
113 $providers[] = 'core';
|
Chris@0
|
114 $storages_to_delete = [];
|
Chris@0
|
115
|
Chris@0
|
116 // Gather fields that will be deleted during configuration synchronization
|
Chris@0
|
117 // where the module that provides the field type is also being uninstalled.
|
Chris@0
|
118 $field_storage_ids = [];
|
Chris@0
|
119 foreach ($deletes as $config_name) {
|
Chris@0
|
120 $field_storage_config_prefix = \Drupal::entityManager()->getDefinition('field_storage_config')->getConfigPrefix();
|
Chris@0
|
121 if (strpos($config_name, $field_storage_config_prefix . '.') === 0) {
|
Chris@0
|
122 $field_storage_ids[] = ConfigEntityStorage::getIDFromConfigName($config_name, $field_storage_config_prefix);
|
Chris@0
|
123 }
|
Chris@0
|
124 }
|
Chris@0
|
125 if (!empty($field_storage_ids)) {
|
Chris@0
|
126 $field_storages = \Drupal::entityQuery('field_storage_config')
|
Chris@0
|
127 ->condition('id', $field_storage_ids, 'IN')
|
Chris@0
|
128 ->condition('module', $providers, 'NOT IN')
|
Chris@0
|
129 ->execute();
|
Chris@0
|
130 if (!empty($field_storages)) {
|
Chris@0
|
131 $storages_to_delete = FieldStorageConfig::loadMultiple($field_storages);
|
Chris@0
|
132 }
|
Chris@0
|
133 }
|
Chris@0
|
134
|
Chris@0
|
135 // Gather deleted fields from modules that are being uninstalled.
|
Chris@14
|
136 /** @var \Drupal\field\FieldStorageConfigInterface[] $deleted_storage_definitions */
|
Chris@14
|
137 $deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions();
|
Chris@14
|
138 foreach ($deleted_storage_definitions as $field_storage_definition) {
|
Chris@14
|
139 if ($field_storage_definition instanceof FieldStorageConfigInterface && !in_array($field_storage_definition->getTypeProvider(), $providers)) {
|
Chris@14
|
140 $storages_to_delete[$field_storage_definition->id()] = $field_storage_definition;
|
Chris@0
|
141 }
|
Chris@0
|
142 }
|
Chris@0
|
143 return $storages_to_delete;
|
Chris@0
|
144 }
|
Chris@0
|
145
|
Chris@0
|
146 }
|