Last updated April 25, 2014. Created on April 25, 2014.
Edited by mikeryan. Log in to edit this page.

Sometimes you might take the opportunity when migrating to rearrange your vocabularies a bit - consolidate multiple similar terms to a single term, rename some terms, drop other terms, etc. One approach is to setup the mappings of old terms to new terms in a CSV file included with your migration module, and lookup incoming term names against that data in prepareRow().

Let's say we want to consolidate the "Bop", "Bebop", and "Swing" terms into "Jazz" (which also already exists in the source), rename "Rock" to "Rock & Roll", and drop any other terms. We would have a music.csv file looking like:

Rock,"Rock & Roll"

   * Implements Migration::prepareRow().
  public function prepareRow($row) {
    if (parent::prepareRow($row) === FALSE) {
      return FALSE;

    // Read the crosswalk file the first time through.
    if (empty($this->crosswalk)) {
      $filename = DRUPAL_ROOT . '/' . drupal_get_path('module', 'my_migration_module')
                  . '/music.csv';
      $handle = fopen($filename, 'r');
      if ($handle) {
        while ($data = fgetcsv($handle)) {
          $legacy_name = $data[0];
          $new_name = $data[1];
          $this->crosswalk[$legacy_name] = $new_name;

    // If we find a crosswalk for this term, map to that tid, otherwise we
    // fall through and import the term normally (assigning the new name).
    if (isset($this->crosswalk[$row->name])) {
      $new_name = $this->crosswalk[$row->name];
      $query = db_select('taxonomy_vocabulary', 'v')
        ->condition('machine_name', $this->arguments['destination_vocabulary']);
      $query->innerJoin('taxonomy_term_data', 'td', 'v.vid=td.vid');
      $new_tid = $query->fields('td', array('tid'))
        ->condition('', $new_name)
      if ($new_tid) {
        $this->map->saveIDMapping($row, array($new_tid),
          MigrateMap::STATUS_IGNORED, MigrateMap::ROLLBACK_PRESERVE);
        $this->rollbackAction = MigrateMap::ROLLBACK_PRESERVE;
        return FALSE;
      else {
        $row->name = $new_name;
        $row->parent = 0;
    else {
      // Don't import anything not in the crosswalk.
      return FALSE;

    return TRUE;

Naturally, you could substitute a database table for the CSV - the advantage of the CSV is it's simple for non-technical stakeholders, perhaps using a spreadsheet, to submit their desired mappings.

Looking for support? Visit the forums, or join #drupal-support in IRC.