Mercurial > hg > isophonics-drupal-site
diff core/modules/field/src/ConfigImporterFieldPurger.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/modules/field/src/ConfigImporterFieldPurger.php Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,146 @@ +<?php + +namespace Drupal\field; + +use Drupal\Core\Config\ConfigImporter; +use Drupal\Core\Config\Entity\ConfigEntityStorage; +use Drupal\field\Entity\FieldStorageConfig; + +/** + * Processes field purges before a configuration synchronization. + */ +class ConfigImporterFieldPurger { + + /** + * Processes fields targeted for purge as part of a configuration sync. + * + * This takes care of deleting the field if necessary, and purging the data on + * the fly. + * + * @param array $context + * The batch context. + * @param \Drupal\Core\Config\ConfigImporter $config_importer + * The config importer. + */ + public static function process(array &$context, ConfigImporter $config_importer) { + if (!isset($context['sandbox']['field'])) { + static::initializeSandbox($context, $config_importer); + } + + // Get the list of field storages to purge. + $field_storages = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete')); + // Get the first field storage to process. + $field_storage = reset($field_storages); + if (!isset($context['sandbox']['field']['current_storage_id']) || $context['sandbox']['field']['current_storage_id'] != $field_storage->id()) { + $context['sandbox']['field']['current_storage_id'] = $field_storage->id(); + // If the storage has not been deleted yet we need to do that. This is the + // case when the storage deletion is staged. + if (!$field_storage->isDeleted()) { + $field_storage->delete(); + } + } + field_purge_batch($context['sandbox']['field']['purge_batch_size'], $field_storage->uuid()); + $context['sandbox']['field']['current_progress']++; + $fields_to_delete_count = count(static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete'))); + if ($fields_to_delete_count == 0) { + $context['finished'] = 1; + } + else { + $context['finished'] = $context['sandbox']['field']['current_progress'] / $context['sandbox']['field']['steps_to_delete']; + $context['message'] = \Drupal::translation()->translate('Purging field @field_label', ['@field_label' => $field_storage->label()]); + } + } + + /** + * Initializes the batch context sandbox for processing field deletions. + * + * This calculates the number of steps necessary to purge all the field data + * and saves data for later use. + * + * @param array $context + * The batch context. + * @param \Drupal\Core\Config\ConfigImporter $config_importer + * The config importer. + */ + protected static function initializeSandbox(array &$context, ConfigImporter $config_importer) { + $context['sandbox']['field']['purge_batch_size'] = \Drupal::config('field.settings')->get('purge_batch_size'); + // Save the future list of installed extensions to limit the amount of times + // the configuration is read from disk. + $context['sandbox']['field']['extensions'] = $config_importer->getStorageComparer()->getSourceStorage()->read('core.extension'); + + $context['sandbox']['field']['steps_to_delete'] = 0; + $fields = static::getFieldStoragesToPurge($context['sandbox']['field']['extensions'], $config_importer->getUnprocessedConfiguration('delete')); + foreach ($fields as $field) { + $row_count = \Drupal::entityManager()->getStorage($field->getTargetEntityTypeId()) + ->countFieldData($field); + if ($row_count > 0) { + // The number of steps to delete each field is determined by the + // purge_batch_size setting. For example if the field has 9 rows and the + // batch size is 10 then this will add 1 step to $number_of_steps. + $how_many_steps = ceil($row_count / $context['sandbox']['field']['purge_batch_size']); + $context['sandbox']['field']['steps_to_delete'] += $how_many_steps; + } + } + // Each field possibly needs one last field_purge_batch() call to remove the + // last field and the field storage itself. + $context['sandbox']['field']['steps_to_delete'] += count($fields); + + $context['sandbox']['field']['current_progress'] = 0; + } + + /** + * Gets the list of fields to purge before configuration synchronization. + * + * If, during a configuration synchronization, a field is being deleted and + * the module that provides the field type is being uninstalled then the field + * data must be purged before the module is uninstalled. Also, if deleted + * fields exist whose field types are provided by modules that are being + * uninstalled their data need to be purged too. + * + * @param array $extensions + * The list of extensions that will be enabled after the configuration + * synchronization has finished. + * @param array $deletes + * The configuration that will be deleted by the configuration + * synchronization. + * + * @return \Drupal\field\Entity\FieldStorageConfig[] + * An array of field storages that need purging before configuration can be + * synchronized. + */ + public static function getFieldStoragesToPurge(array $extensions, array $deletes) { + $providers = array_keys($extensions['module']); + $providers[] = 'core'; + $storages_to_delete = []; + + // Gather fields that will be deleted during configuration synchronization + // where the module that provides the field type is also being uninstalled. + $field_storage_ids = []; + foreach ($deletes as $config_name) { + $field_storage_config_prefix = \Drupal::entityManager()->getDefinition('field_storage_config')->getConfigPrefix(); + if (strpos($config_name, $field_storage_config_prefix . '.') === 0) { + $field_storage_ids[] = ConfigEntityStorage::getIDFromConfigName($config_name, $field_storage_config_prefix); + } + } + if (!empty($field_storage_ids)) { + $field_storages = \Drupal::entityQuery('field_storage_config') + ->condition('id', $field_storage_ids, 'IN') + ->condition('module', $providers, 'NOT IN') + ->execute(); + if (!empty($field_storages)) { + $storages_to_delete = FieldStorageConfig::loadMultiple($field_storages); + } + } + + // Gather deleted fields from modules that are being uninstalled. + /** @var \Drupal\field\FieldStorageConfigInterface[] $field_storages */ + $field_storages = entity_load_multiple_by_properties('field_storage_config', ['deleted' => TRUE, 'include_deleted' => TRUE]); + foreach ($field_storages as $field_storage) { + if (!in_array($field_storage->getTypeProvider(), $providers)) { + $storages_to_delete[$field_storage->id()] = $field_storage; + } + } + return $storages_to_delete; + } + +}