Skip to content

Instantly share code, notes, and snippets.

@GuyPaddock
Forked from crittermike/modulename.install.php
Created October 19, 2020 15:19
Show Gist options
  • Save GuyPaddock/091d80c67397ee09560e647cdd9fe734 to your computer and use it in GitHub Desktop.
Save GuyPaddock/091d80c67397ee09560e647cdd9fe734 to your computer and use it in GitHub Desktop.
Uninstall Multiversion and Workspace from Drupal 8. This is an update hook which successfully uninstalled the module from our fairly complicated site.
<?php
/**
* Uninstall Multiversion and related modules.
*/
function modulename_update_8000() {
// !!! IMPORTANT !!!
// First, make sure to check and see if any of the below entities have a
// value of NULL in "_deleted" or "workspace" columns, because if so,
// you'll want to update that. See this issue for more information:
// https://www.drupal.org/project/multiversion/issues/3144466
// These are all of the entity types that we CAN run disableEntityTypes() on
// without any data loss. The others we will handle separately below.
$entity_types_to_disable = [
'redirect',
'menu_link_content',
'file',
'shortcut',
'taxonomy_term',
'media',
'crop',
];
$entity_type_manager = \Drupal::entityTypeManager();
$multiversion_manager = \Drupal::service('multiversion.manager');
foreach ($entity_types_to_disable as $entity_type_id) {
if ($entity_type_manager->getDefinition($entity_type_id, FALSE) === NULL) {
_multiversion_remover_print_status(
t('Skipping entity type @type (not installed)',
['@type' => $entity_type_id])
);
continue;
}
_multiversion_remover_print_status(
t('Disabling Multiversion for entity type @type',
['@type' => $entity_type_id])
);
$entity_type =
$entity_type_manager->getStorage($entity_type_id)->getEntityType();
$multiversion_manager->disableEntityTypes([
$entity_type_id => $entity_type,
]);
}
// Delete all the Workspace entities so that the module can be uninstalled.
$entity_types_to_delete = [
'workspace',
'workspace_pointer',
];
foreach ($entity_types_to_delete as $entity_type_id) {
if ($entity_type_manager->getDefinition($entity_type_id, FALSE) === NULL) {
_multiversion_remover_print_status(
t('Skipping workspace entity type @type (not installed)',
['@type' => $entity_type_id])
);
continue;
}
_multiversion_remover_print_status(
t('Deleting workspace entity type @type', ['@type' => $entity_type_id])
);
$controller = $entity_type_manager->getStorage($entity_type_id);
$entities = $controller->loadMultiple();
if (!empty($entities)) {
$controller->delete($entities);
}
}
// Uninstall Multiversion and dependent modules.
$modules = [
'workspace',
'multiversion',
'replication',
'relaxed',
];
$module_handler = \Drupal::moduleHandler();
/** @var \Drupal\Core\Extension\ModuleInstallerInterface $module_installer */
$module_installer = \Drupal::service('module_installer');
foreach ($modules as $module) {
if ($module_handler->moduleExists($module)) {
_multiversion_remover_print_status(
t('Uninstalling module @module', ['@module' => $module])
);
$module_installer->uninstall([$module]);
}
else {
_multiversion_remover_print_status(
t('Skipping uninstall of module @module (not installed)',
['@module' => $module])
);
}
}
// Now we have to manually remove the Multiversion-powered field storage
// definitions for the entity types that we could NOT run disableEntityTypes()
// on. This way we accomplish the same thing but without the migration that
// Multiversion does, which was causing lots of corrupt and lost data.
$entity_definitions = \Drupal::keyValue('entity.definitions.installed');
$definitions_to_update = [
'block_content',
'node',
'paragraph',
];
foreach ($definitions_to_update as $definition) {
$fields = $entity_definitions->get("$definition.field_storage_definitions");
unset($fields['workspace']);
unset($fields['_deleted']);
unset($fields['_rev']);
$entity_definitions->set("$definition.field_storage_definitions", $fields);
}
// Now we need to delete any entities of those entity types which were
// marked as _deleted. This is another thing that disableEntityTypes() would
// have done for us if we could have used it on these entity types.
$database = \Drupal::database();
$tables = [
'node_field_data' => [
'alias' => 'n',
'id_field' => 'nid',
'entity_type' => 'node',
],
'block_content_field_data' => [
'alias' => 'b',
'id_field' => 'id',
'entity_type' => 'block_content',
],
'paragraphs_item_field_data' => [
'alias' => 'p',
'id_field' => 'id',
'entity_type' => 'paragraph',
],
];
foreach ($tables as $table_name => $table_info) {
$query = $database->select($table_name, $table_info['alias']);
$query->fields($table_info['alias'], [$table_info['id_field']]);
// We only need to delete the entities where _deleted = 1, meaning they
// were already manually deleted but Multiversion was keeping them around
// because that's what Multiversion does.
$query->condition($table_info['alias'] . '._deleted', 1);
$result = $query->execute();
$entity_ids = [];
foreach ($result as $row) {
$entity_ids[] = $row->{$table_info['id_field']};
}
$storage = $entity_type_manager->getStorage($table_info['entity_type']);
$entities = $storage->loadMultiple($entity_ids);
$storage->delete($entities);
}
// And finally, delete the Multiversion-powered fields on the entity tables.
// Not doing this won't actually cause any problems, but they are useless
// so we may as well get rid of them.
$schema = \Drupal::database()->schema();
$tables = [
'node_field_data',
'block_content_field_data',
'paragraphs_item_field_data',
];
$fields = [
'workspace',
'_deleted',
'_rev',
];
foreach ($tables as $table) {
foreach ($fields as $field) {
$schema->dropField($table, $field);
}
}
// Rebuild the taxonomy_index table in case the uninstallation
// process cleared it out.
$query = \Drupal::entityQuery('node');
$ids = $query->accessCheck(FALSE)->execute();
$storage_handler = $entity_type_manager->getStorage('node');
// If you have a lot of entities on your site, you may need to use
// array_chunk() here to split this up to avoid loading all nodes
// at the same time.
$entities = $storage_handler->loadMultiple($ids);
/** @var \Drupal\node\NodeInterface[] $entities */
foreach ($entities as $entity) {
taxonomy_build_node_index($entity);
}
}
/**
* Logs a progress message, then displays it if this was invoked via Drush CLI.
*
* All messages are logged at DEBUG level.
*
* @param string $message
* The progress message to log.
*/
function _multiversion_remover_print_status($message) {
\Drupal::logger('multiversion_remover')
->debug($message);
if (function_exists('drush_print')) {
// @codingStandardsIgnoreLine
/** @noinspection PhpMethodParametersCountMismatchInspection */
drush_print($message);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment