Last updated October 29, 2013. Created by mikeryan on January 3, 2011.
Edited by agentrickard, michaellenahan, q0rban, Letharion. Log in to edit this page.

Suppose you are migrating articles from an external site into Drupal article nodes. These articles may reference each other, so in Drupal you have created a node reference field on articles, field_article_ref. The references may be circular - article A may reference article B, and B in return references A. Now, the natural way to set this up in your Migration class is:

<?php
class ArticleMigration extends Migration {
  public function
__construct() {
...
   
$this->addFieldMapping('field_article_ref', 'ref_id')
         ->
sourceMigration(array('Article'));
...
?>

sourceMigration() takes an array of migration machine names as an argument.

This tells the Migrate module to use ref_id (the ID of the referenced article in the source system) to lookup the Drupal node that was migrated from the referenced article, and to use that node's ID to populate field_article_ref. But, when you're importing article A, article B hasn't been imported yet, so there is no Drupal node to use to populate field_article_ref. By default, field_article_ref will be left empty.

The Migrate module provides a solution to this chicken-and-egg problem: stub nodes. If the migration referenced in sourceMigration() implements a createStub() method, which creates a dummy node and returns its ID, then if the lookup of ref_id fails createStub will be called and the resulting node used to fill in field_article_ref. Then, when article B finally runs through the migration, the actual content of article B will replace the dummy data and everything will be hunky-dory.

Your createStub method should look something like:

<?php
 
protected function createStub(Migration $migration, array $source_id) {
   
$node = new stdClass();
   
$node->title = t('Stub for @id', array('@id' => $source_id[0]));
   
$node->body[LANGUAGE_NONE][0] = t('Stub body');
   
$node->type = $this->destination->getBundle();
   
$node->uid = 1;
   
$node->status = 0;
   
node_save($node);
    if (isset(
$node->nid)) {
      return array(
$node->nid);
    }
    else {
      return
FALSE;
    }
  }
?>

Pretty simple - create a node object, fill in the essential data, and save it.

If you need to handle things more dynamically -- for example, if you're pulling the source ID out of other fields -- you can call the method behind ->sourceMigration() directly:

<?php
 
public function prepareRow($row) {
    ...
   
$source_id = $this->findReferencedNid($row->body);
   
$row->ref_id = $this->handleSourceMigration('SourceMigration', $source_id);
    ...
  }
?>

Note that if you're doing it yourself, you should NOT append ->sourceMigration to the field mapping.

Using other destinations

The stub solution will also work for non-node migrations. Any migration class can use stubs. (Files, however, may not work as expected.)

For example, if you were to pull in Menu Links for a migration, you may find that some parent links don't exist. In that case, you can use stubs to create the parent item.

<?php
 
function createStub(Migration $migration, array $source_id) {
   
$link = array(
     
'menu_name' => 'mymenu',
     
'link_path' => 'user', // must be a valid path
     
'link_title' => 'Stub',
    );
    if (
$mlid = menu_link_save($link)) {
      return array(
$mlid);
    }
    else {
      return
FALSE;
    }
  }
?>

In this case, the $source_id would be the parent id of the menu link being saved by the current migration row.

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

Comments

Wow, this was really nifty. Imagine someone has solved the chicken-egg-problem. Lovely.

It should be mentioned, this currently does not work for files.
See #1314748-6: Files need to be resaved on update

The migration will create new files, instead of updating existing ones.

I have an instance where a node can reference itself. So creating a stub node will not work without some clean up afterwards. How might this be achieved?

I had a similar issue migrating book nodes. The short answer is to use parent::handleSourceMigration('MigrationName', $source_nid); in a complete() function to lookup the migrated nid only when required.

Have a look at my post about migrating book nodes for more detail: http://drupal.org/node/1513766

Thanks! Very helpful information.

Note that the body field needs to have a language and delta. For example:

<?php
$node
->body[LANGUAGE_UNDEFINED][0] = t('Stub body');
?>

Media module was very unhappy about the code in the original article.

--Darryl Richman
Crafty Fox Software
http://darryl.crafty-fox.com

I needed to import multiple fields to a node reference field that also had the issue of multiple source rows, as in Multiple source data rows. It ended up being really easy, I just combined the separator and the sourceMigration and it worked perfectly.

$this->addFieldMapping('field_stuff', 'stuff_id')->separator(',')->sourceMigration(array('MigrateStuff'));

Hello !

When my migration creates stubs, my nodes get an auto-path generated from the temporary title that has been set in the code. But when the stub is updated by the real node values, there's no update of the path even if I can see that pathauto is still set to TRUE (well 1) and path set to empty (ie. '').

I've tried something like the following but it's not working :s

<?php
 
public function complete($entity, $row) {
   
pathauto_node_update_alias($entity, 'update');
  }
?>

Does someone know how I can achieve an update of the path during a migration update using pathauto ?

EDIT : I am such an idiot sometimes. There was no update of the path because pathauto_update_action was set to 0... It works fine now !