Skip to content

Instantly share code, notes, and snippets.

@crittermike
Last active January 14, 2021 06:08
Show Gist options
  • Save crittermike/a60804a33b8c8e5e3489e7d72a3912af to your computer and use it in GitHub Desktop.
Save crittermike/a60804a33b8c8e5e3489e7d72a3912af 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',
];
$multiversionManager = \Drupal::service('multiversion.manager');
foreach ($entity_types_to_disable as $entity_type_id) {
echo "Disabling Multiversion for " . $entity_type_id . "\n";
$entity_type = \Drupal::entityTypeManager()->getStorage($entity_type_id)->getEntityType();
$multiversionManager->disableEntityTypes([$entity_type_id => $entity_type]);
}
// Delete all the Workspace entities so that the module can be uninstalled.
$entity_types = [
'workspace',
'workspace_pointer',
];
foreach ($entity_types as $type) {
$defs = \Drupal::entityManager()->getDefinitions();
if (!empty($defs[$type])) {
$controller = \Drupal::entityManager()->getStorage($type);
if ($entities = $controller->loadMultiple()) {
$controller->delete($entities);
}
}
}
// Uninstall Multiversion and dependent modules.
$modules = [
'workspace',
'multiversion',
'replication',
'relaxed',
];
\Drupal::service('module_installer')->uninstall($modules);
// 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 = \Drupal::entityTypeManager()->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 = \Drupal::entityTypeManager()->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);
foreach ($entities as $entity) {
taxonomy_build_node_index($entity);
}
}
@iamtheghost
Copy link

Great work, Mike! I am running into an error when running this against my app running core 8.6.9 plus a very old version of multiversion:

>  [notice] Update started: ukh_mv_uninstall_update_8100
>  [warning] count(): Parameter must be an array or an object that implements Countable ContentEntityStorageTrait.php:265
>  [warning] count(): Parameter must be an array or an object that implements Countable ContentEntityStorageTrait.php:301
>  [error]  Drupal\Core\Entity\EntityStorageException: Table mapping contains invalid field workspace. in Drupal\Core\Entity\Sql\SqlContentEntityStorage->mapToStorageRecord() (line 962 of /Users/MyUsername/Sites/devdesktop/redacted-dev/docroot/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php). 
>  [warning] Attempt to assign property 'new_edit' of non-object ContentEntityStorageTrait.php:179
>  [error]  Drupal\Core\Entity\EntityStorageException: Table mapping contains invalid field workspace. in Drupal\multiversion\Entity\Storage\Sql\ContentEntityStorage->save() (line 180 of /Users/MyUsername/Sites/devdesktop/redacted-dev/docroot/modules/contrib/multiversion/src/Entity/Storage/ContentEntityStorageTrait.php). 
>  [error]  Table mapping contains invalid field workspace. 
>  [error]  Update failed: ukh_mv_uninstall_update_8100 
 [error]  Update aborted by: ukh_mv_uninstall_update_8100 
 [error]  Finished performing updates. 

Any idea what this could be getting hung up on? My db tables with the workspace column are these:
DB Tables

It takes aroun 1.5 hours to throw this error, so I assume the script is actually working but my app is configured slightly differently or the antiquated version of Multiversion is causing the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment