Migrate/Migrated2d: Linking Migrated Nodes To Non-migrated Terms

Migrate/Migrated2d: Linking Migrated Nodes To Non-migrated Terms

Submitted by Raymond Mbuyi on Mon, 07/28/2014 - 22:52
Drupal migration image

Migrate and Migrate d2d modules expose a powerful API to developers who have the task to migrate a part or an entire website to Drupal 7; these two modules come in handy whenever the migration task is subject to complex business rules. One short article is far from being enough to offer an in-depth look at the countless opportunities offered by these two modules. Therefore, this article will explain how to solve a specific migration issue, hoping in the process, to help  grasp what can be done with these two modules.

For more information regarding the migrate/migrated2d API, click here.

The title of the article is self-explanatory, so here is a description of the design approach before diving in.

  • Identifying unmigrated terms that are mapped to the node.
  • Unlinking the node and the terms before the node migration.
  • Saving the IDs of the terms in a static variable.
  • Relinking the node and the terms right after persisting the node.

Also, I need to mention that prepareRow() and complete() belong to the migration base classes; prepareRow() is called very early in the process before importing each node and complete() is called right after persisting each node.

To start, create a method to unlink terms from node and then call it in prepareRow() right after saving terms per vocabulary in separate arrays. Since this process will be repeated for each nodes, creating a getter/setter for taxonomy related processes will prove helpful.

Term IDs Getter/Setter

public function tids_getter_setter ($voc = null ,$term_to_add = null, $resetting = false, $returning = false, $assigning = false) {
    static $term_ids = array();
    if (!empty($assigning)) {
      $term_ids[$voc][] = $term_to_add;
    } elseif ($resetting) {
      $term_ids[$voc] = null;     
    } elseif ($returning) {
      return $term_ids[$voc];

Function unlinking terms and node

public function rm_unmigrated_terms($ids_to_check, $migrate_map_table, $row, $vocab_var_name, $migration_class, $field_vocab) {
    if (!empty($ids_to_check)) {
      $query = db_select($migrate_map_table, 'commkeys')
              ->condition('commkeys.sourceid1', $ids_to_check, 'IN')
              ->fields('commkeys', array('sourceid1', 'destid1'));
      $result = $query->execute()->fetchAll();
    if (!empty($result)) {
      foreach ($result as $term_tids) {
        if (empty($term_tids->destid1)) {
          // trick to get the key of the tid to remove from $row
          if (($key = array_search($term_tids->sourceid1, $ids_to_check)) !== false) {
            // save  the source tid in a static variable then remove it from $row. the tid from static variable will be used in complete()
            $migration_class->tids_getter_setter($vocab_var_name, $ids_to_check[$key], false, false, true);
    return $row;

The above function is called below, in prepareRow()

public function prepareRow($row) {
  if (parent::prepareRow($row) === FALSE) {
    return FALSE; 
  $keywords_ids_to_check = $row->field_keywords;
  $topics_ids_to_check = $row->field_topicterms;
  $migration_class = $this;
  // remove unmigrated tags from $row
  $row = $this->rm_unmigrated_terms($tags_ids_to_check, 'migrate_map_twtags', $row, 'migrate_tags_tids', $migration_class, 'field_tags');
  // remove unmigrated keywords from $row
  $row = $this->rm_unmigrated_terms($keywords_ids_to_check, 'migrate_map_twkeywords', $row, 'migrate_keywords_tids', $migration_class, 'field_keywords');

Function relinking terms and node

public function linking_stories_to_unmigrated_terms($terms_ids, $vocab_id, $vocab_var_name, $migration_class, $node) {
  //getting names of terms to map
  if (!empty($terms_ids)) {
    $query = db_select('d7_techwell.taxonomy_term_data', 'twttd')
             ->condition('twttd.tid', $terms_ids)
             ->fields('twttd', array('name'))
    // reset static variable to avoid that the value for current story migration be used during next node migration
    $migration_class->tids_getter_setter($vocab_var_name, 0, true, false, false);
    if (!empty($query)) {
      foreach ($query as $term_name) {
        $query2 = db_select('d7_sqe_communities.taxonomy_term_data', 'comttd')
                  ->condition('comttd.vid', $vocab_id)
                  ->condition('comttd.name', $term_name->name)
                  ->fields('comttd', array('tid'))
        // adding mapping between nodes and terms
        $query3 = db_insert('d7_sqe_communities.taxonomy_index')
                      'nid' => $node->nid,
                      'tid' => $query2[0]->tid,
                      'sticky' => 0,
                      'created' => REQUEST_TIME

Finally, in complete(), gathering the information needed to relink node and terms then calling the above function to do it

function complete($node, stdClass $row) {
  $migration_class = $this;
  // linking node to unmigrated topic terms in target
  $topics_to_link = $migration_class->tids_getter_setter('migrate_topics_tids', 0, false, true, false);
  $tags_to_link = $migration_class->tids_getter_setter('migrate_tags_tids', 0, false, true, false);
  $keywords_to_link = $migration_class->tids_getter_setter('migrate_keywords_tids', 0, false, true, false);
  if (!empty($topics_to_link)) {
    $this->linking_stories_to_unmigrated_terms($topics_to_link, '2', 'migrate_topics_tids', $migration_class, $node);
  // linking node to unmigrated keywords terms in target
  if (!empty($tags_to_link)) {
    $this->linking_stories_to_unmigrated_terms($tags_to_link, '11', 'migrate_keywords_tids', $migration_class, $node);
  // linking node to unmigrated tags terms in target
  if (!empty($keywords_to_link)) {
    $this->linking_stories_to_unmigrated_terms($keywords_to_link, '17', 'migrate_tags_tids', $migration_class, $node);