Hi. Just getting into working with Migrate 2. I'll feel a bit embarrassed if there's an example amongst the beer and wine that I missed, but I'm struggling a bit to wrap my head around how to deal with node reference fields. My particular scenario might also be contributing to my confusion.

To contrive a (rather pitiful) example of what I'm dealing with, let's say I've got a table about cars, and I'm trying to ultimately normalize into Car and related Manufacturer nodes. So table might look like this:

Car ID, Description, Manufacturer Name, Some Other Manufacturer Detail
1, My fine Model T, Ford, Only makes cars in black
2, 55 Bel Air, Chevy, As American as Mom and apple pie
3, Some other Model T, Ford, Only makes cars in black

...and at the end of migrating, I'll want to end up with 3 Car nodes and 2 Manufacturer nodes (basically, creating any Manufacturer nodes I don't already have, and then referencing them from Car).

Looking at the Economist migration article (http://drupal.org/node/915102) it sounds like there was quite a bit of node ref fun to be had. I'm hoping someone might be able to walk me through the best way of dealing with this in Migrate 2 (or if I should go back and look at Migrate 1 or some other means). RTFM/RTFC-type pointers gladly accepted if available, but overall approach/steps guidance and/or examples also greatly appreciated.

Thanks in advance for your help!

-Brant

Comments

moshe weitzman’s picture

Status: Active » Fixed

You will create two migrations. The source table for each will be the same. The first migration creates manufacturer nodes. The second migration creates Car nodes. order the migrations such that car depends on manufacturer. That way, the node references will work fine.

feel free to reopen and ask more questions.

mikeryan’s picture

Moshe got in just ahead of me... One more point to make, for the node reference field mapping in the car migration, you need to include ->sourceMigration('Manufacturer') (where 'Manufacturer' is the machine name of your manufacturer migration), to translate the manufacturer name (which you would use as the key in your manufacturer migration) to the node ID of the manufacturer node.

brant’s picture

Ok, that helps -- was wondering if that might be the approach, thanks! Two follow-ups:

1. Is there an example I overlooked in the module examples (or elsewhere) that shows a node reference field mapping and such?

2. Where does the idea of stub nodes mentioned in the Economist article come into play?

Thanks so much for getting me pointed in the right direction (and for the amazing work here)!

brant’s picture

Status: Fixed » Active
moshe weitzman’s picture

Status: Active » Fixed

Stub nodes are only needed when you have node refs that can't easily be done in order such as a 'related cars' field on car nodes. Not needed in your example.

Search for if (module_exists('node_reference')) in beer.inc for Drupal 7. You will see an example there. Not sure why thats not in D6 beer.inc.

brant’s picture

Thanks much for the help -- will check it out.

brant’s picture

Status: Fixed » Active

Hi Guys-

After a few epic battles, I'm making some headway with Migrate. One nasty issue I've run into with my node refs, though. If the widget type for my node ref field is a select box, life is good. However, if the widget type is set to autocomplete text field, the node ref will not be set during import (have confirmed by trying the two widgets on the same field). We're using autocomplete quite a bit in conjunction with the snazzy node relationships module functionality, so I really don't want to have to switch back to select boxen. Any pointers for how I might work around this issue? Hoping I'm just not configuring something properly.

Thanks much for your continued assistance.

-Brant

brant’s picture

Apologies -- that is not correct. This particular node ref is not working for the select box either (I got fooled by it defaulting to the first person in the list). Sorry about that. More in a sec -- I think I've spotted another difference.

brant’s picture

Status: Active » Closed (fixed)

Sigh. I'm all good now. Move along. Nothing to see here. :)

(thanks, and sorry for the trouble)

Anonymous’s picture

Hi brant,

Were you able to get node reference working? Or did you have to resort to using a different method?

I have looked into the Drupal 7 way and I have tried using the different widgets and I can not seem to get the Node Reference field to populate on import. The referenced nodes have already been created and imported into the system.

The node I am trying to import to has 4 node reference fields and 2 user reference fields.

FrequenceBanane’s picture

subscribe

brant’s picture

Sorry for the incredibly delayed response. I'm afraid I don't recall what ended up being my own bug in getting the node ref fields to work, but in case it helps, here's a somewhat stripped down example where I imported Counties that were subsequently referenced from Districts:

abstract class MyBaseMigration extends Migration {
  public function __construct() {
    // Always call the parent constructor first for basic setup
    parent::__construct();

    // With migrate_ui enabled, migration pages will indicate people involved in
    // the particular migration, with their role and contact info. We default the
    // list in the shared class; it can be overridden for specific migrations.
    $this->team = array(
      new MigrateTeamMember('Fred G. Sanford', 'example@example.com', t('Implementer'))
    );

  }
}

class CountyMigration extends MyBaseMigration {
  public function __construct() {
    parent::__construct();
    $this->description = t('Migrate Counties');

    $this->map = new MigrateSQLMap($this->machineName,
      array(
        'name' => array(
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
          'description' => 'County Name',
          'alias' => 'c',
        )
      ),
      MigrateDestinationNode::getKeySchema()
    );
    
    $query = db_select( 'feeds_data_counties', 'c' )
             -> fields( 'c', array('name') );

    $this->source = new MigrateSourceSQL( $query, array() );

    // Set up our destination - nodes of type county
    $this->destination = new MigrateDestinationNode('county');

    // Mapped fields
    $this->addFieldMapping( 'title', 'name' );
    $this->addFieldMapping( 'uid' )
         ->defaultValue(1);
  }
}

class DistrictMigration extends MyBaseMigration {
  public function __construct() {
    parent::__construct();
    $this->description = t('Migrate Districts');
    
    $this->dependencies = array('County','LegislativeSession');

    $this->map = new MigrateSQLMap($this->machineName,
      array(
        'district_sortable' => array(
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
          'description' => 'District Sortable',
          'alias' => 'd',
        )
      ),
      MigrateDestinationNode::getKeySchema()
    );
    
    $query = db_select( 'feeds_data_districts', 'd' );
    $query->addField( 'd', 'district_sortable' );
    $query->addField( 'd', 'districtformal' );
    $query->addField( 'd', 'counties' );
    // add id_num for debug purposes only
    $query->addField( 'd', 'id_num' );

    $this->source = new MigrateSourceSQL($query, array() );

    // Set up our destination - nodes of type district
    $this->destination = new MigrateDestinationNode('district');

    // Mapped fields
    $this->addFieldMapping( 'title', 'districtformal' );
    $this->addFieldMapping( 'field_sortable_name', 'district_sortable' );
    $this->addFieldMapping( 'field_counties_in_district', 'counties_in_district' );

    // node ref
    $this->addFieldMapping( 'field_counties', 'counties' )
         ->sourceMigration('County')
         ->separator('|'); 
    
    // author  
    $this->addFieldMapping( 'uid' )
         ->defaultValue(1);
  }
    
}