Tutorial: Controlling the Order of Drupal Updates in Custom Modules

A lot of Drupal websites get by without having to mess around with manual updates. For some sites / platforms though, the hook_update_N process is critical for making and deploying changes into the cloud. And this introduces an interesting challenge: what happens if you have multiple updates that you need to deploy, and what happens if those updates need to happen in a particular order? This short tutorial will help you establish a clear order of updates (when needed) so you can tightly control the order in which those updates are executed.

Understanding Update Hooks

Update hooks are a key part of the Drupal ecosystem. They live in the .install file for modules, and can do “just about anything” to a Drupal site (which is a pretty broad spectrum of possibilities). The big take away is that you should be getting into the habit of running your updates anytime you deploy your site. This should accommodate any changes you and your team have made to your modules, as well as any updates that may have come in via Drupal core / contrib updates.

Example updates from recent site update.

Some examples of things I’ve done in recent memory with update hooks include:

  • seeding a Taxonomy with a desired list of terms

  • install / uninstall modules

  • create / edit / delete Drupal configuration

  • move video links from a field on nodes into a media bundle

  • create / edit / delete Drupal content

You can see some of these examples in practice on the Drupal Govcon site’s repository.

How to Write an Update Hook

Update hooks are one of the simplest things to do in a Drupal module context. They are intended to do a very specific thing, in a very specific context, and tend to be quite simple. Don’t misunderstand, you can really screw up a Drupal website with an update hook (so it’s critical that you take them seriously and thoroughly test).

Here’s an example of a fairly simple update hook that automatically creates a set of taxonomy terms:

/**
 * DCG-107: Add terms to sponsorship level vocabulary.
 */
function capitalcamp_glue_update_8001() {
  $sponsorshipLevelTerms = [
    'Core',
    'Coffee',
    'Contributor',
    'Mentor',
    'Sprinter',
    'Exhibitor',
    'Supporter',
    'Personal',
    'Training',
  ];
  foreach ($sponsorshipLevelTerms as $sponsorshipLevelTerm) {
    Term::create([
      'name' => $sponsorshipLevelTerm,
      'vid' => 'sponsorship_level',
    ])->save();
  }
}

In this example, when the drush updb command is run (or Drupal database updates are executed via the UI), if this 8001 update hasn’t already executed, Drupal will execute it automatically. As this code executes, it essentially will iterate through my sponsorshipLevelTerms array and then use the Drupal\taxonomy\Entity\Term class’ create method to create each term in the array and insert directly into the sponsorship_level vocabulary.

In this particular scenario, I didn’t worry about a lot of error handling or duplicate checking, since I knew for sure that there were no terms in the vocabulary in production. Obviously your miles may (and probably will) vary, so you may need to add additional validation / error handling before you take action. This is particularly critical on a multisite platform.

Controlling Update Ordering

The biggest challenge with the above update hook, is that Drupal will just pick an order for updating. If you have a sequence of updates (let’s say, another update that creates the sponsorship_level vocabulary in the first place) that must run ahead of another one (that inserts terms into the new vocabulary) then we have a problem.

On the one hand, we could just do everything in a single giant update. This isn’t a great idea in some scenarios. So this takes into update dependencies.

TLDR, you can implement hook_update_dependencies in your custom module’s .install file and tell Drupal which updates are dependent on which others. Normally you would do this between modules, since if you have control within the module in question of the order they will run in (based on the numbering schema).

As an example, maybe our glue 8001 update should only happen after another update in the capital camp conference year module runs. We can define this as such:

/**
 * Implements hook_update_dependencies().
 */

function capitalcamp_glue_update_dependencies() {
  $dependencies['capitalcamp_glue'][8001] = array(
    'capitalcamp_conference_year' => 8000,
  );

  return $dependencies;
}

Conclusion

As with any properly architected code, updates should be small in scope and easily testable in the proper context. This necessarily will create many more updates for complex actions, but thanks to the dependencies, that’s ok!

Related Content