annotate core/modules/field/src/ConfigImporterFieldPurger.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 1fec387a4317
children
rev   line source
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 }