Making Database Updates Using hook_update_n in Drupal
drush ev 'print \Drupal::keyValue("system.schema")->get("crystalbit_entity_updates") . "\n";'
drush ev "\Drupal::keyValue('system.schema')->set('crystalbit_entity_updates', (int) 11102)";
function MYMODULE_update_9001() {
$modules_to_disable = ['module_name', 'other_module_name', 'another_module_name',];
$config_name = 'core.extension';
// Load the existing configuration.
$config = \Drupal::configFactory()->getEditable($config_name);
// Get the current list of enabled modules.
$enabled_modules = $config->get('module') ?? [];
// Disable the specified modules.
foreach ($modules_to_disable as $module) {
if (isset($enabled_modules[$module])) {
unset($enabled_modules[$module]);
}
}
// Update the configuration with the new list of enabled modules.
$config->set('module', $enabled_modules);
$config->save();
}
In Drupal, hook_update_n is the standard way to perform database schema changes, data migrations, or configuration updates across environments. These functions are stored in your module's .install file and are triggered when you run drush updb or navigate to /update.php.
The "n" in the function name follows a specific numbering convention: module_update_8001.
8: The core version (e.g., 8, 9, or 10).
0: The major release version.
01: The sequential number of the update.
1. Basic Structure
Add this to your my_module.install file.
PHP
/**
* Add a new 'status' column to the 'my_custom_table' table.
*/
function my_module_update_9001() {
$schema = \Drupal::database()->schema();
$spec = [
'type' => 'int',
'description' => 'The status of the entry.',
'length' => 1,
'not null' => TRUE,
'default' => 0,
];
$schema->addField('my_custom_table', 'status', $spec);
}
2. Using the Batch-like functionality in Updates
If you need to update thousands of records (e.g., a data migration), you should not do it all at once, or the script will timeout. hook_update_n supports a $sandbox variable that works exactly like the Batch API.
PHP
/**
* Migrating data from field_old to field_new for all 'article' nodes.
*/
function my_module_update_9002(&$sandbox) {
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
// Initialize the sandbox on the first run.
if (!isset($sandbox['total'])) {
$nids = $node_storage->getQuery()
->condition('type', 'article')
->accessCheck(FALSE)
->execute();
$sandbox['total'] = count($nids);
$sandbox['ids'] = array_values($nids);
$sandbox['current'] = 0;
}
$nodes_per_batch = 25;
$batch_ids = array_slice($sandbox['ids'], $sandbox['current'], $nodes_per_batch);
foreach ($node_storage->loadMultiple($batch_ids) as $node) {
// Perform data logic
$old_value = $node->get('field_old')->value;
$node->set('field_new', $old_value);
$node->save();
$sandbox['current']++;
}
// Tell Drupal how far along we are.
// When #finished reaches 1, the update is complete.
$sandbox['#finished'] = ($sandbox['current'] >= $sandbox['total']) ? 1 : ($sandbox['current'] / $sandbox['total']);
return t('Processed @current of @total nodes.', [
'@current' => $sandbox['current'],
'@total' => $sandbox['total'],
]);
}
3. Common Update Tasks
Task | Method to use |
Add a Table |
|
Drop a Column |
|
Change Column Type |
|
Update Config |
|
4. Best Practices
Never Change an Existing Update: Once a
hook_update_nhas been deployed and run on a server, do not edit it. If you need to fix a mistake, write a new update function with a higher number.Access Check: When using entity queries in updates, always use
->accessCheck(FALSE)because updates often run as an anonymous user via CLI.Dependencies: If your update depends on another module's update being run first, you can't strictly enforce it via code numbering, but you can check for the existence of tables or configuration before proceeding.
Pro-Tip: The "Entity Definition" Update
If you added a new field via a .yml file and want Drupal to recognize the new schema for an existing entity type, you might need the entity_definition_update_manager service:
PHP
$update_manager = \Drupal::entityDefinitionUpdateManager();
$field_storage_definition = $update_manager->getFieldStorageDefinition('my_field', 'node');
$update_manager->installFieldStorageDefinition('my_field', 'node', 'my_module', $field_storage_definition);Additional later discussion: how to use hook_post_update_NAME and how it differs from hook_update_n
Recent content
-
3 hours 53 minutes ago
-
1 week 1 day ago
-
1 week 1 day ago
-
1 week 1 day ago
-
1 week 1 day ago
-
1 week 6 days ago
-
2 weeks 1 day ago
-
2 weeks 1 day ago
-
2 weeks 1 day ago
-
2 weeks 2 days ago