Migrate Classes: Location CCK to Address Field

I've just begun to tackle migrating a site from Drupal 6 to Drupal 7 via V2 of the Migrate module. It's truly an excellent module, but like most things Drupal, it has a steep learning curve. To help offset that slope, I plan to post migration snippets on this blog throughout the next few weeks. The snippets should help to serve as starting points for the many different migration scenarios that you all may encounter. Here is a short list of tentatively planned examples:

  1. Migrating from Location CCK fields (i.e. mapadelic) to Address Fields.
  2. Migrating from Content Profile nodes to Profile2 entities.
  3. Migrating Organic Groups memberships

Please note that this is not a tutorial for the Migrate module. If you'd like a detailed explanation of the Migrate API, please check either the examples in the Migrate module, or read this excellent blog post on the migrate module.

First up, Location CCK to Address Field!

<

p>It seems that the Address Field module has taken a strong lead in the realm on Drupal geolocation, so after a bit of research, I've decided to jump on the bandwagon. Here's a bare bones migration class that will query a base node table, join the necessary mapadelic location tables, and migrate them into an address field. Please note that you will require the Migrate Extras module to perform this migration.

<?php
<?php

class grasmashNodeSericeProviderMigration extends Migration {
  public function
__construct() {
   
parent::__construct();

    $this->description = t('Migrate location-based nodes.');
   
$this->dependencies = array('grasmashUser');

    $this->map = new MigrateSQLMap($this->machineName,
      array(
       
'nid' => array(
         
'type' => 'int',
         
'unsigned' => TRUE,
         
'not null' => TRUE,
         
'description' => 'D6 Unique Node ID',
         
'alias' => 'n',
        )
      ),
     
MigrateDestinationNode::getKeySchema()
    );

    $query = db_select(GRASMASH_MIGRATION_DATABASE_NAME . '.node', 'n')
      ->
fields('n', array('nid', 'vid', 'type', 'language', 'title', 'uid', 'status', 'created', 'changed', 'comment', 'promote', 'moderate', 'sticky', 'tnid', 'translate'))
      ->
condition('n.type', 'service_provider', '=');
   
$query->join(GRASMASH_MIGRATION_DATABASE_NAME . '.node_revisions', 'nr', 'n.vid = nr.vid');
   
$query->addField('nr', 'body');
   
$query->addField('nr', 'teaser');
   
$query->addField('nr', 'format');
   
$query->join(GRASMASH_MIGRATION_DATABASE_NAME . '.users', 'u', 'n.uid = u.uid');
   
$query->addField('u', 'name');
   
$query->leftjoin(GRASMASH_MIGRATION_DATABASE_NAME . '.location_instance', 'li', 'n.nid = li.nid AND n.vid = li.vid');
   
$query->addField('li', 'lid');
   
$query->leftjoin(GRASMASH_MIGRATION_DATABASE_NAME . '.location', 'l', 'li.lid = l.lid');
   
$query->addField('l', 'street');
   
$query->addField('l', 'additional');
   
$query->addField('l', 'city');
   
$query->addField('l', 'province');
   
$query->addField('l', 'postal_code');
   
$query->addField('l', 'country');
   
$query->addField('l', 'latitude');
   
$query->addField('l', 'longitude');
   
$query->orderBy('n.nid', 'ASC');

    $source_fields = array(
     
'nid' => t('The node ID of the page'),
     
'uid' => t('The user ID of a user'),
     
'lid' => t('The location ID of a location instance'),
    );

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

    $this->destination = new MigrateDestinationNode('service_provider');

    // Assign mappings TO destination fields FROM source fields.
   
$this->addFieldMapping('is_new')->defaultValue(TRUE);
   
$this->addFieldMapping('title', 'title');
   
$this->addFieldMapping('nid', 'nid');
   
$this->addFieldMapping('uid', 'uid');
   
$this->addFieldMapping('revision')->defaultValue(TRUE);
   
$this->addFieldMapping('revision_uid', 'uid');
   
$this->addFieldMapping('created', 'created');
   
$this->addFieldMapping('changed', 'changed');
   
$this->addFieldMapping('status', 'status');
   
$this->addFieldMapping('promote', 'promote');
   
$this->addFieldMapping('sticky', 'sticky');
   
$this->addFieldMapping('comment', 'comment');
   
$this->addFieldMapping('path', 'url_alias');
   
$this->addFieldMapping('language')->defaultValue(LANGUAGE_NONE);

    // Map to addressfield. See addressfield.inc in migrate_extras for available argument keys.
   
$arguments = array(
     
'thoroughfare' => array('source_field' => 'street'),
     
'premise' => array('source_field' => 'additional'),
     
'locality' => array('source_field' => 'city'),
     
'administrative_area' => array('source_field' => 'province'),
     
'postal_code' => array('source_field' => 'postal_code'),
     
'first_name' => array('source_field' => 'field_name_first'),
     
'last_name' => array('source_field' => 'field_name_last'),
    );
   
// Note that of the country field is NULL, none of the values will be migrated!
   
$this->addFieldMapping('field_address', 'country')->arguments($arguments);
   
   
// Since the excerpt is mapped via an argument, add a null mapping so it's not flagged as unmapped.
   
$this->addFieldMapping(NULL, 'street');
   
$this->addFieldMapping(NULL, 'additional');
   
$this->addFieldMapping(NULL, 'city');
   
$this->addFieldMapping(NULL, 'province');
   
$this->addFieldMapping(NULL, 'postal_code');
   
$this->addFieldMapping(NULL, 'country');
   
$this->addFieldMapping(NULL, 'latitude');
   
$this->addFieldMapping(NULL, 'longitude');
   
$this->addFieldMapping(NULL, 'lid');
   
   
// The body text.
   
$body_arguments = MigrateTextFieldHandler::arguments(array('source_field' => 'teaser'), array('source_field' => 'format'));
   
$this->addFieldMapping('body', 'body')->arguments($body_arguments);

    // Unmapped source fields
   
$this->addUnmigratedSources(array('vid', 'type', 'language', 'moderate', 'tnid', 'translate', 'teaser', 'format', 'name'));
  }

  public function prepareRow($current_row) {
   
// Set the text format for the node.
   
$current_row->format = 'wysiwyg_ckeditor';
    return
TRUE;
  }
}
?>

In addition to creating this migration class, I also added a quick strtoupper() wrapper to line 56 of addressfield.inc file, packaged with the migrate_extras module:

<?php
$return
[$language][$delta] = array('country' => strtoupper($value)) + array_intersect_key($arguments, $field_info['columns']);
?>

I'm fairly certain that this can be better accomplished with migrate's prepareRow() method, but I haven't had time to fool with it yet.

Good luck!

Drupal Version Compatibility: 

Comments

Nice walkthrough of the migration module. Technically, addressfield does not do geolocation by itself, it only holds postal addresses. A good way to actually place those addresses on a map is to use a combo of the geofield and geocoder modules. These two modules will populate your site with geocoded data which can be used to power either an Openlayers map (using the Openlayers module) or a simple google map.

Just curious why you choose to change the field?

I chose to change it for a few reasons:

  • Address field is more lightweight
  • A stable version of the mapadelic module suite is not yet available for Drupal 7
  • Address field is integrated with more Drupal 7 modules

There may be other reasons as well. I was primarily concerned about getting locked into using the Location module before seeing a stable, well supported release.

i was looking for some address migration code and this is very helpful

Hello,

Do you know how to grab a cell value from a CSV file, manipulate the data first, and then map that altered value to a CCK field?

Basic mappings from a cell to a CCK field are easy:
$this->addFieldMapping('field_school_note_2', 'note2');

Mapping a static value to a CCK field is easy:
$this->addFieldMapping('field_is_active')
->defaultValue(TRUE);

But what if I wanted to do something really interesting, like grab the value of a cell and 3 other cells in the same row, run some logic on those four values to construct something custom, and then save that special value to the field_school_note_2 CCK field, how would I do that? I can't seem to find any examples online on how to do this.

If you know the answer and can post an example here, that would be awesome!

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.