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