fredag den 29. august 2014

Renumbering hook_update_N

I've on more than one occasion come across hook_update_N's that weren't enumerated correctly.

Normally this doesn't cause much of a problem, but wrong update hook numbering just hurts my soul.

The documentation states: "Never renumber update hooks". Well ... screw that :-). Let's try and do it anyway, in a sensible and hopefully elegant manner.


The old and wrong hook_update_N numbering could look like this:
/**
 * Do stuff ...
 */
function mymodule_update_7000(&$sandbox) {
  // Do stuff ...
}

/**
 * Do stuff ...
 */
function mymodule_update_7001(&$sandbox) {
  // Do stuff ...
}

/**
 * Do stuff ...
 */
function mymodule_update_7100(&$sandbox) {
  // Do stuff ...
}


7000 and 7001 are wrong in this case. Someone has then skipped to 7100 in an attempt to fix the numbering. But has obviously failed. What we really want is:

/**
 * Do stuff ...
 */
function mymodule_update_7101(&$sandbox) {
  // Do stuff ...
}

/**
 * Do stuff ...
 */
function mymodule_update_7102(&$sandbox) {
  // Do stuff ...
}

/**
 * Do stuff ...
 */
function mymodule_update_7103(&$sandbox) {
  // Do stuff ...
}


How do we get from the old to the new?

/**
 * Mapping of schema versions (old => new).
 *
 * @return array
 */
function _mymodule_get_mapping() {
  return array(
    7000 => 7101,
    7001 => 7102,
    7100 => 7103,
  );
}

/**
 * Implements hook_requirements().
 *
 * Check and fix schema version before updating.
 */
function mymodule_requirements($phase) {
  switch ($phase) {
    case 'update':
      _mymodule_renumber_schema_version();
      break;
  }
}

/**
 * Drush does not invoke hook_requirements('update') for modules other
 * than 'system'.
 *
 * Attempt to intercept the command updatedb (updb), and fix the schema
 * version if applicable.
 */
if (drupal_is_cli() && function_exists('drush_get_command')) {
  $command = drush_get_command();
  if (isset($command['command']) && $command['command'] == 'updatedb') {
    _mymodule_renumber_schema_version();
  }
}

/**
 * Renumber current schema version according to mapping.
 */
function _mymodule_renumber_schema_version() {
  $schema_version = _mymodule_get_current_schema_version();
  $mapping = _mymodule_get_mapping();

  if (!empty($mapping[$schema_version])) {
    $new_version = $mapping[$schema_version];
    drupal_set_installed_schema_version('mymodule', $new_version);
  }
}

/**
 * Helper function for getting the current schema version.
 */
function _mymodule_get_current_schema_version() {
  return db_select('system', 's')
    ->fields('s', array('schema_version'))
    ->condition('name', 'mymodule')
    ->execute()
    ->fetchField();
}

/**
 * Do stuff ...
 */
function mymodule_update_7101(&$sandbox) {
  // Do stuff ...
}

/**
 * Do stuff ...
 */
function mymodule_update_7102(&$sandbox) {
  // Do stuff ...
}

/**
 * Do stuff ...
 */
function mymodule_update_7103(&$sandbox) {
  // Do stuff ...
}



Caveat: The might very well be some slight problems regarding hook_update_dependencies(), if other modules implement this towards your module.