Chickens and eggs: using stubs

Last updated on
30 April 2025

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:

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:

  protected function createStub($migration, array $source_id) {
    $node = new stdClass();
    $node->title = t('Stub for @id', array('@id' => $source_id[0]));
    $node->body[LANGUAGE_NONE][0]['value'] = 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:

  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.

  function createStub($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.

Help improve this page

Page status: Not set

You can: