Why would I want to add content in an update hook?

If you’ve ever worked on a project in a team setting that uses the local/development/staging workflow, you probably know that it can be difficult to keep test content synced between databases. One way to handle this is to add content in one environment and sync the database between the other environments. Keep in mind that this method is fine and will be unavoidable once the client begins adding content to the site. There are times, though, when I like to add content programmatically, using an update hook in a custom module or feature. It could be helpful if you’re adding content that will be needed for QA testing; preventing duplication of the content creation process and ensuring that the test case will be uniform. Second, it’s helpful if you’re creating content that you know will be needed from the beginning of a site build, for example, a blog “Category” vocabulary.

Wait, what’s an update hook?

You may be thinking, “what is an update hook?” Basically, an update hook allows us to perform actions on a site’s database that only need to happen once. If you’ve ever updated Drupal core or a contributed module, you’ve probably run database updates. When you navigate to /update.php on your site, Drupal checks to see if any modules, themes, or profiles have update hooks that need to run. Update hooks are just functions, and what they do can range from simple tasks like enabling a module to complex tasks like modifying tables in the database. Once an update hook is run, Drupal notes the update hook’s number and then avoids running it again in the future. Magic! For more magical learning, there are plenty of existing resources available on update hooks.

The scenario

I love baseball, and more specifically, I love the Chicago Cubs. I’m not one of the many fairweather Cubs fans. I have watched most games through the good seasons (a few of these) and the bad seasons (a lot of these) since high school. If you didn’t know, the Cubs were surprisingly fantastic last year and are projected to be one of the best teams in the league this coming season. That being said, there have been a lot of bad years and the Cubs haven’t won a World Series since 1908. If my great great great grandfather (whose name I don’t know) were around, I’d love to talk to him about it. For this example, let’s say we’re building a Cubs fan site. One of the features of the site is an archive of all known news articles related to the Cubs. We want to be able to organize these articles by source as well as the year of the season.

The example

Let’s start with a basic update hook that we are going to add for our custom cubs_archive module. Update hooks always go inside of the module’s .install file. So, we’ll create a file called cubs_archive.install.

<?php
/**
* @file
* Installation and update functions for the Cubs Archive custom module.
*/

From here, we’ll declare the update hook and leave it empty for now. Notice that the update function has a number on the end. Drupal keeps track of updates it has already run by setting the schema_version column of the system table entry for your module in the site’s database to the latest update number that has been run. This is a new module, so we’ll start from 7101 (think version 7.x-1.0). The next update we run will be 7102, and so the process goes.

/**
* An update hook to create Season and Source taxonomy terms.
*/
function cubs_archive_update_7101() {
// Our code will go here. }

Next, we’ll build the functionality inside of this update in three steps:

  • First, we’ll list the machine names of the vocabularies that we want to use and load them.
/**
* An update hook to create Season and Source taxonomy terms.
*/
function cubs_archive_update_7101() {
// I'm listing the machine names of the vocabularies I want to use.
$vocabularies = array(
'season',
'source',
);   foreach ($vocabularies as $vocabulary_name) {
  // I'm loading the vocabulary to get the the vocabulary's ID.
  $vocabulary = taxonomy_vocabulary_machine_name_load($vocabulary_name);
  }
}
  • Next, we’ll list the names of the terms that we want to create for each vocabulary. because the Cubs have been around in one form or another since 1876, I’m going to save some time and use a loop to create those terms.
/**
* An update hook to create Season and Source taxonomy terms.
*/
function cubs_archive_update_7101() {
// We add the machine names of the vocabularies we're going to use.
$vocabularies = array(
'season',
'source',
); foreach ($vocabularies as $vocabulary_name) {
// We load the vocabulary to get the the vocabulary ID.
$vocabulary = taxonomy_vocabulary_machine_name_load($vocabulary_name);
$terms = array();
if ($vocabulary->machine_name == 'source') {
// We add an array of term names that we want to create.
$terms = array(
'Newspaper',
'Magazine',
'Blog',
);
}
elseif ($vocabulary->machine_name == 'season') {
// Rather than typing years out manually, we use a loop to create them.
for ($season = 1876; $season <= date("Y"); $season++) {
// I've included the quotes around the variable on purpose.
// It converts the integer to a string, which is what we want.
$terms[] = "$season";
}
}
}
}
  • Lastly, let’s prepare the term object and save each of them.
/**
 * @file
 * Installation and update functions for the Cubs Archive custom module.
 */
/**
 * An update hook to create Season and Source taxonomy terms.
 */
function cubs_archive_update_7101() {
  // We add the machine names of the vocabularies we're going to use.
  $vocabularies = array(
    'season',
    'source',
  );   foreach ($vocabularies as $vocabulary_name) {
    // We load the vocabulary to get the the vocabulary ID.
    $vocabulary = taxonomy_vocabulary_machine_name_load($vocabulary_name);
    $terms = array();
    if ($vocabulary->machine_name == 'source') {
      // We add an array of term names that we want to create.
      $terms = array(
        'Newspaper',
        'Magazine',
        'Blog',
      );
    }
    elseif ($vocabulary->machine_name == 'season') {
      // Rather than typing years out manually, we use a loop to create them.
    for ($season = 1876; $season <= date("Y"); $season++) {
      $terms[] = "$season";
}
    }
    foreach ($terms as $term_name) {
    // This prepares the term object for saving.
    $term = new stdClass();
    $term->name = $term_name;
    $term->vid = $vocabulary->vid;
    // This creates the actual taxonomy term.
    taxonomy_term_save($term);
  }
  }

Voila! Now, if we navigate to /update.php or run “drush updb” (if you use drush), our update will run and we’ll have all of these newly-created terms ready to be used. Even better, our development team will also have these terms available when they merge our code and run updates, no database syncing necessary. Just like that, we’re prepped and ready to use the newly created terms to build the archive feature which covers the rich and at times utterly depressing history of the Cubs. Go Cubs go!

Tips

  • You might be thinking that this method is going to take longer than just adding the content manually. That might be the case the first few times that you do this, but you’ll get faster the more you do it.
     

  • Related to the previous tip, I suggest keeping a note of “common update hooks” so that you have quick access to common update hooks you find yourself using. When you create a new type of update hook, go ahead and add it to the list. They can be easily adapted for your current use case and dropped in as a new update.
     

  • This is probably obvious, but make sure you test this locally. Our example doesn’t add any fields to the term except the title, but you may have a situation where there are required fields. Plan ahead for those.

Additional Resources
Best Practices for Custom Modules | Blog Post
Your Drupal Site is a Platform | Blog Post
Using Cloud Hooks on Acquia Cloud Hosting | Blog Post
A First Look at Behavior Driven Development with Behat in Drupal | Blog Post