function hook_update_N

Same name and namespace in other branches
  1. 7.x modules/system/system.api.php \hook_update_N()
  2. 9 core/lib/Drupal/Core/Extension/module.api.php \hook_update_N()
  3. 8.9.x core/lib/Drupal/Core/Extension/module.api.php \hook_update_N()
  4. 10 core/lib/Drupal/Core/Extension/module.api.php \hook_update_N()

Perform a single update between minor versions.

Modules should use hook hook_update_N() to update between minor or major versions of the module. Sites upgrading from Drupal 6 or 7 to any higher version should use the Migrate API instead.

Naming and documenting your function

For each change in a module that requires one or more actions to be performed when updating a site, add a new implementation of hook_update_N() to your my_module.install file (assuming my_module is the machine name of your module). Implementations of hook_update_N() are named (module name)_update_(number).

The number (N) must be higher than hook_update_last_removed().

@todo Remove reference to CORE_MINIMUM_SCHEMA_VERSION (e.g., x001) once https://www.drupal.org/project/drupal/issues/3106712 is resolved.

Examples:

  • node_update_8001(): The first update for the Drupal 8.0.x version of the Drupal Core node module.
  • node_update_81001(): The first update for the Drupal 8.10.x version of the Drupal Core node module.
  • my_module_update_8101(): The first update for your custom or contributed module's 8.x-1.x versions.
  • my_module_update_8201(): The first update for the 8.x-2.x versions.
  • my_module_update_80201(): Alternate five-digit format for the first update for the 8.x-2.x versions. Subsequent update hooks must always be five digits and higher than 80201.
  • my_module_update_8201(): The first update for the custom or contributed module's 2.0.x versions, which are compatible with Drupal 8 and 9.
  • my_module_update_91001(): The first update for the custom or contributed module's 10.0.x versions, which are compatible with Drupal 9.

Never renumber update functions. The numeric part of the hook implementation function is stored in the database to keep track of which updates have run, so it is important to maintain this information consistently.

The documentation block preceding this function is stripped of newlines and used as the description for the update on the pending updates task list, which users will see when they run the update.php script.

Notes about the function body

Writing hook_update_N() functions is tricky. There are several reasons why this is the case:

  • You do not know when updates will be run: someone could be keeping up with every update and run them when the database and code are in the same state as when you wrote your update function, or they could have waited until a few more updates have come out, and run several at the same time.
  • You do not know the state of other modules' updates either.
  • Other modules can use hook_update_dependencies() to run updates between your module's updates, so you also cannot count on your functions running right after one another.
  • You do not know what environment your update will run in (which modules are installed, whether certain hooks are implemented or not, whether services are overridden, etc.).

Because of these reasons, you'll need to use care in writing your update function. Some things to think about:

  • Never assume that the database schema is the same when the update will run as it is when you wrote the update function. So, when updating a database table or field, put the schema information you want to update to directly into your function instead of calling your hook_schema() function to retrieve it (this is one case where the right thing to do is copy and paste the code).
  • Never assume that the configuration schema is the same when the update will run as it is when you wrote the update function. So, when saving configuration, use the $has_trusted_data = TRUE parameter so that schema is ignored, and make sure that the configuration data you are saving matches the configuration schema at the time when you write the update function (later updates may change it again to match new schema changes).
  • Never assume your field or entity type definitions are the same when the update will run as they are when you wrote the update function. Always retrieve the correct version via \Drupal::entityDefinitionUpdateManager()::getEntityType() or \Drupal::entityDefinitionUpdateManager()::getFieldStorageDefinition(). When adding a new definition always replicate it in the update function body as you would do with a schema definition.
  • Be careful about API functions and especially CRUD operations that you use in your update function. If they invoke hooks or use services, they may not behave as expected, and it may actually not be appropriate to use the normal API functions that invoke all the hooks, use the database schema, and/or use services in an update function -- you may need to switch to using a more direct method (database query, etc.).
  • In particular, loading, saving, or performing any other CRUD operation on an entity is never safe to do (they always involve hooks and services).
  • Never rebuild the router during an update function.

The following actions are examples of things that are safe to do during updates:

  • Cache invalidation.
  • Using \Drupal::configFactory()->getEditable() and \Drupal::config(), as long as you make sure that your update data matches the schema, and you use the $has_trusted_data argument in the save operation.
  • Marking a container for rebuild.
  • Using the API provided by \Drupal::entityDefinitionUpdateManager() to update the entity schema based on changes in entity type or field definitions provided by your module.

See https://www.drupal.org/node/2535316 for more on writing update functions.

Batch updates

If running your update all at once could possibly cause PHP to time out, use the $sandbox parameter to indicate that the Batch API should be used for your update. In this case, your update function acts as an implementation of callback_batch_operation(), and $sandbox acts as the batch context parameter. In your function, read the state information from the previous run from $sandbox (or initialize), run a chunk of updates, save the state in $sandbox, and set $sandbox['#finished'] to a value between 0 and 1 to indicate the percent completed, or 1 if it is finished (you need to do this explicitly in each pass).

See the Batch operations topic for more information on how to use the Batch API.

Multiple upgrade paths

There are situations where changes require a hook_update_N() to be applied to different major branches. This results in more than one upgrade path from the current major version to the next major version.

For example, if an update is added to 11.1.0 and 10.4.0, then a site on 10.3.7 can update either to 10.4.0 and from there to 11.1.0, or directly from 10.3.7 to 11.1.1. In one case, the update will run on the 10.x code base, and in another on the 11.x code base, but the update system needs to ensure that it doesn't run twice on the same site.

hook_update_N() numbers are sequential integers, and numbers lower than the modules current schema version will never be run. This means once a site has run an update, for example, 11100, it will not run a later update added as 10400. Backporting of updates therefore needs to allow 'space' for the 10.4.x codebase to include updates which don't exist in 11.x (for example to ensure a later 11.x update goes smoothly).

To resolve this, when handling potential backports of updates between major branches, we use different update numbers for each branch, but record the relationship between those updates in the older branches. This is best explained by an example showing the different branches updates could be applied to:

  • The first update, system_update_10300 is applied to 10.3.0.
  • Then, 11.0.0 is released with the update removed, system_update_last_removed() is added which returns 10300.
  • The next update, system_update_11100, is applied to 11.1.x only.
  • Then 10.4.0 and 11.1.0 are released. system_update_11100 is not backported to 11.0.x or any 10.x branch.
  • Finally, a critical data loss update is necessary. The bug-fix supported branches are 11.1.x, 11.0.x, and 10.4.x. This results in adding the updates system_update_10400 (10.4.x), system_update_11000 (11.0.x) and system_update_11101 (11.1.x) and making the 10.4.1, 11.0.1 and 11.1.1 releases.

This is a list of the example releases and the updates they contain:

  • 10.3.0: system_update_10300
  • 10.4.1: system_update_10300 and system_update_10400 (equivalent to system_update_11101)
  • 11.0.0: No updates
  • 11.0.1: system_update_11000 (equivalent to system_update_11101)
  • 11.1.0: system_update_11100
  • 11.1.1: system_update_11100 and system_update_11101

In this situation, sites on 10.4.1 or 11.0.1 will be required to update to versions that contain system_update_11101. For example, a site on 10.4.1 would not be able to update to 11.0.0, because that would result in it going 'backwards' in terms of database schema, but it would be able to update to 11.1.1. The same is true for a site on 11.0.1.

The following examples show how to implement a hook_update_N() that must be skipped in a future update process.

Future updates can be marked as equivalent by adding the following code to an update.

function my_module_update_10400() {
    \Drupal::service('update.update_hook_registry')->markFutureUpdateEquivalent(11101, '11.1.1');
    // The rest of the update function.
}

At the moment we need to add defensive coding in the future update to ensure it is skipped. @code function my_module_update_11101() { $equivalent_update = \Drupal::service('update.update_hook_registry')->getEquivalentUpdate(); if ($equivalent_update instanceof \Drupal\Core\Update\EquivalentUpdate) { return $equivalent_update->toSkipMessage(); }

// The rest of the update function. } @encode

Parameters

array $sandbox: Stores information for batch updates. See above for more information.

Return value

string|null Optionally, update hooks may return a translated string that will be displayed to the user after the update has completed. If no message is returned, no message will be presented to the user.

Throws

\Drupal\Core\Utility\UpdateException|PDOException In case of error, update hooks should throw an instance of Drupal\Core\Utility\UpdateException with a meaningful message for the user. If a database query fails for whatever reason, it will throw a PDOException.

See also

hook_update_last_removed()

The numbers are normally composed of three parts:

  • 1 or 2 digits for Drupal core compatibility (Drupal 8, 9, 10, etc.). This convention must be followed. If your module is compatible with multiple major versions (e.g., it has a core_version_requirement of '^8.8 || ^9'), use the lowest major core branch it is compatible with (8 in this example).
  • 1 or 2 digits for your module's major release version. Examples:
    • For 8.x-1.* or 1.y.x (semantic versioning), use 1 or 01.
    • For 8.x-2.* or 2.y.x, use 2 or 02.
    • For 8.x-10.* or 10.y.x, use 10.
    • For core 8.0.x, use 0 or 00.
    • For core 8.1.x, use 1 or 01.
    • For core 8.10.x, use 10.

    This convention is optional but suggested for clarity. Note: While four- digit update Hooks are standard, if you follow the above convention, you may run into the need to start using five digits if you get to a major (or, for core, a minor) version 10. This will work fine; what matters is that you must ALWAYS increase the number when a new update hook is added. For example, if you add hook_update_81001(), you cannot later add hook_update_9101(). Instead, you would need to use hook_update_90101().

  • 2 digits for sequential counting, starting with 01. Note that the x000 number can never be used: the lowest update number that will be recognized and run is 8001.

Batch operations

Schema API

hook_update_last_removed()

update_get_update_list()

\Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface

https://www.drupal.org/node/2535316

Related topics

117 functions implement hook_update_N()

Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.

block_content_update_last_removed in core/modules/block_content/block_content.install
Implements hook_update_last_removed().
block_update_last_removed in core/modules/block/block.install
Implements hook_update_last_removed().
comment_node_update_index in core/modules/comment/comment.module
Implements hook_node_update_index().
comment_update_last_removed in core/modules/comment/comment.install
Implements hook_update_last_removed().
content_moderation_update_last_removed in core/modules/content_moderation/content_moderation.install
Implements hook_update_last_removed().

... See full list

File

core/lib/Drupal/Core/Extension/module.api.php, line 769

Code

function hook_update_N(&$sandbox) {
    // For non-batch updates, the signature can simply be:
    // function hook_update_N() {
    // Example function body for adding a field to a database table, which does
    // not require a batch operation:
    $spec = [
        'type' => 'varchar',
        'description' => "New Col",
        'length' => 20,
        'not null' => FALSE,
    ];
    $schema = Database::getConnection()->schema();
    $schema->addField('my_table', 'newcol', $spec);
    // Example of what to do if there is an error during your update.
    if ($some_error_condition_met) {
        throw new UpdateException('Something went wrong; here is what you should do.');
    }
    // Example function body for a batch update. In this example, the values in
    // a database field are updated.
    if (!isset($sandbox['progress'])) {
        // This must be the first run. Initialize the sandbox.
        $sandbox['progress'] = 0;
        $sandbox['current_pk'] = 0;
        $sandbox['max'] = Database::getConnection()->query('SELECT COUNT([my_primary_key]) FROM {my_table}')
            ->fetchField();
    }
    // Update in chunks of 20.
    $records = Database::getConnection()->select('my_table', 'm')
        ->fields('m', [
        'my_primary_key',
        'other_field',
    ])
        ->condition('my_primary_key', $sandbox['current_pk'], '>')
        ->range(0, 20)
        ->orderBy('my_primary_key', 'ASC')
        ->execute();
    foreach ($records as $record) {
        // Here, you would make an update something related to this record. In this
        // example, some text is added to the other field.
        Database::getConnection()->update('my_table')
            ->fields([
            'other_field' => $record->other_field . '-suffix',
        ])
            ->condition('my_primary_key', $record->my_primary_key)
            ->execute();
        $sandbox['progress']++;
        $sandbox['current_pk'] = $record->my_primary_key;
    }
    $sandbox['#finished'] = empty($sandbox['max']) ? 1 : $sandbox['progress'] / $sandbox['max'];
    // To display a message to the user when the update is completed, return it.
    // If you do not want to display a completion message, return nothing.
    return t('All foo bars were updated with the new suffix');
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.