I gave up with the Addressfield Migration Class and decided to do a db_insert -- and it worked!
Not only works, but because I register the insert in the prepare() function of class SchoolMigration (where I was already using db queries) it also rolls back!
<?php
/**
* @file migrate_users.migrate.inc
* Our own hook implementation.
*/
/**
* Migration classes for migrating users and user fields
*
* see Migrate handbook: https://drupal.org/migrate
* and http://www.verbosity.ca/working-drupals-migrate-module
*/
/* useful functions for debugging using drush migrate commands:
drush_print_r($row);
drush_print_r($this->destinationValues);
*/
/**
* Implements hook_migrate_api()
*
* Returns 'api' => 2 for the 7.x-2.x branch of Migrate.
* Registers the migration classes for the 7.x-2.6 branch of Migrate (including
* 7.x-2.6+xx-dev).
*/
function migrate_users_migrate_api() {
$api = array(
'api' => 2,
// Migrations can be organized into groups. The key used here will be the
// machine name of the group, which can be used in Drush:
// drush migrate-import --group=MigrateMembers
// The title is a required argument which is displayed for the group in the
// UI. You may also have additional arguments for any other data which is
// common to all migrations in the group.
'groups' => array(
'MigrateMembersGroup' => array(
'title' => t('Import Member Users'),
),
),
// Here we register the individual migrations. The keys (MigrateMembers_Step1,
// etc.) are the machine names of the migrations, and the class_name
// argument is required. The group_name is optional (defaulting to 'default')
// but specifying it is a best practice.
'migrations' => array(
'MigrateMembers' => array(
'class_name' => 'MigrateMembers',
'group_name' => 'MigrateMembersGroup',
),
'GraduationYear' => array(
'class_name' => 'GraduationYearMigration',
'group_name' => 'MigrateMembersGroup',
),
'School' => array(
'class_name' => 'SchoolMigration',
'group_name' => 'MigrateMembersGroup',
),
'SchoolID' => array(
'class_name' => 'SchoolIDMigration',
'group_name' => 'MigrateMembersGroup',
),
),
);
return $api;
}
/**
* Abstract class as a base for all our migration classes
*/
abstract class MigrateMembers_Basic_Migration extends Migration {
public function __construct($arguments) {
// Always call the parent constructor first for basic setup
parent::__construct($arguments);
// Avoid known line ending issue: drupal.org/node/1705850
ini_set('auto_detect_line_endings', TRUE);
}
}
/**
* User-only migration
* (and creates $row (Row) with user id to key other tables in subsequent migration classes)
*
* The data file is assumed to be in
* sites/all/modules/migrate_users/data_sources/
* the columns in the csv must be in the same order left-to-right as the $columns array below
* every row in csv must have a unique id in the first column (can be ascending numbers, for example)
*/
class MigrateMembers extends MigrateMembers_Basic_Migration {
public function __construct($arguments) {
parent::__construct($arguments);
$this->description = t('Import an CSV-file (only "User Account"-fields)');
$columns = array(
// Format: ('Fieldname', t('Description'))
0 => array('Row', t('Row number (must be unique)')),
1 => array('Type', t('S = Student M = Member')),
2 => array('Prefix', t('Mr. Mrs. etc')),
3 => array('FirstName', t('First Name')),
4 => array('MiddleName', t('MiddleName')),
5 => array('LastName', t('Last Name')),
6 => array('StaffNotes', t('Staff Notes')),
7 => array('SchoolNameAffiliation', t('SchoolName Affiliation')),
8 => array('ProfessionalAffiliation', t('Professional Affiliation ')),
9 => array('AddressLine1', t('Address Line 1')),
10 => array('AddressLine2', t('Address Line 2')),
11 => array('City', t('City')),
12 => array('State', t('State')),
13 => array('ZipCode', t('Zip Code')),
14 => array('Country', t('Country')),
15 => array('TelephoneNumber', t('Telephone Number')),
16 => array('EMAIL', t('Email')),
17 => array('Birthday', t('Birthday')),
18 => array('SchoolID', t('SchoolID')),
19 => array('Descriptor', t('Descriptor')),
20 => array('GraduationYear', t('Graduation Year')),
21 => array('SchoolAffiliation', t('SchoolAffiliation')),
);
// TIP: delete ", array('header_rows' => 1)" in the next line, if the CSV-file has NO header-line
$this->source = new MigrateSourceCSV(DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_users') . '/data_sources/drupaluser_import.csv', $columns, array('header_rows' => 1));
$this->destination = new MigrateDestinationUser();
$this->map = new MigrateSQLMap($this->machineName,
array('Row' => array( // this field is used to connect user to other tables
'type' => 'varchar',
'length' => 6,
'not null' => TRUE,
'description' => t('User\'s Member-ID') // description never used
)
),
MigrateDestinationUser::getKeySchema()
);
// Mapped fields
$this->addFieldMapping('name', 'EMAIL')
->defaultValue('')
->description(t('Name (User Account)'));
$this->addFieldMapping('mail', 'EMAIL')
->defaultValue('')
->description(t('Email address'));
$this->addFieldMapping('init')
->defaultValue('')
->description(t('Email address used for initial user-account creation'));
$this->addFieldMapping('pass', 'PASSWORD')
->defaultValue('asdf')
->description(t("User's password (plain text)"));
$this->addFieldMapping('is_new')
->defaultValue(TRUE)
->description(t('Build the new user (0|1)'));
$this->addFieldMapping('roles')
->defaultValue(6)
->description(6 . t(' = "member"'));
$this->addFieldMapping('theme')
->defaultValue('')
->description(t("User's default theme"));
$this->addFieldMapping('signature')
->defaultValue('')
->description(t("User's signature"));
$this->addFieldMapping('signature_format')
->defaultValue('filtered_html')
->description(t('Which filter applies to this signature'));
$this->addFieldMapping('created')
->defaultValue(time())
->description(t('unique timestamp of user creation date'));
$this->addFieldMapping('access')
->defaultValue(0)
->description(t('unique timestamp for previous time user accessed the site'));
$this->addFieldMapping('login')
->defaultValue(0)
->description(t('unique timestamp for user\'s last login'));
$this->addFieldMapping('status')
->defaultValue(1)
->description(t('Whether the user is active(1) or blocked(0)'));
$this->addFieldMapping('timezone')
->defaultValue(t('Europe/London')) // 'America/Los_Angeles', 'Europe/Berlin', 'UTC', ... from drupal.org/node/714214
->description(t("User's time zone"));
$this->addFieldMapping('language')
->defaultValue(t('en')) // e.g.: 'en', 'fr', 'de', ...
->description(t("User's default language"));
$this->addFieldMapping('picture')
->defaultValue(0)
->description(t('Avatar of the user'));
// Other handlers
if (module_exists('path')) {
$this->addFieldMapping('path')
->defaultValue(NULL)
->description(t('Path alias'));
}
if (module_exists('pathauto')) {
$this->addFieldMapping('pathauto')
->defaultValue(1)
->description(t('Perform aliasing (set to 0 to prevent alias generation during migration)'));
}
// Unmapped destination fields
$this->addUnmigratedDestinations(array('role_names', 'data'));
}
}
class GraduationYearMigration extends MigrateMembers_Basic_Migration {
public function __construct($arguments) {
parent::__construct($arguments);
$this->description = 'Add data to field_data_field_graduation_year';
$this->dependencies = array('MigrateMembers');
$table_name = 'field_data_field_graduation_year';
$this->source = new MigrateSourceCSV(DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_users') . '/data_sources/drupaluser_import.csv', $columns, array('header_rows' => 1));
$this->destination = new MigrateDestinationTable($table_name);
$this->map = new MigrateSQLMap($this->machineName,
array('Row' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
)
),
MigrateDestinationTable::getKeySchema($table_name)
);
// Mapped fields
// add uid from completed MigrateMembers migration
$this->addFieldMapping('entity_id', 'Row')->sourceMigration('MigrateMembers');
$this->addFieldMapping('revision_id', 'Row')->sourceMigration('MigrateMembers');
// Insert Graduation Year value
$this->addFieldMapping('field_graduation_year_value', 'SchoolAffiliation')->sourceMigration('MigrateMembers')
->defaultValue(date("Y"));
// set some defaults from the schema
$this->addFieldMapping('delta')
->defaultValue("0");
$this->addFieldMapping('entity_type')
->defaultValue("user");
$this->addFieldMapping('bundle')
->defaultValue("user");
$this->addFieldMapping('language')
->defaultValue("und");
$this->removeFieldMapping('path');
$this->removeFieldMapping('pathauto');
}
}
class SchoolMigration extends MigrateMembers_Basic_Migration {
public function __construct($arguments) {
parent::__construct($arguments);
$this->description = 'Add data to field_data_field_member_school';
$this->dependencies = array('MigrateMembers');
$table_name = 'field_data_field_member_school';
$this->source = new MigrateSourceCSV(DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_users') . '/data_sources/drupaluser_import.csv', $columns, array('header_rows' => 1));
$this->destination = new MigrateDestinationTable($table_name);
$this->map = new MigrateSQLMap($this->machineName,
array('Row' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
)
),
MigrateDestinationTable::getKeySchema($table_name)
);
$this->addFieldMapping('entity_id', 'Row')->sourceMigration('MigrateMembers');
$this->addFieldMapping('revision_id', 'Row')->sourceMigration('MigrateMembers');
$this->addFieldMapping('field_member_school_tid', 'SchoolAffiliation')->sourceMigration('MigrateMembers');
// set some defaults from the schema
$this->addFieldMapping('delta')
->defaultValue("0");
$this->addFieldMapping('entity_type')
->defaultValue("user");
$this->addFieldMapping('bundle')
->defaultValue("user");
$this->addFieldMapping('language')
->defaultValue("und");
$this->removeFieldMapping('path');
$this->removeFieldMapping('pathauto');
}
public function prepareRow($row){
// convert 3 letter codes to ISO 3166-1 standard 2 letter codes http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes
// TODO:
// 1 - check for full list of country codes in old data
// 2 - many records with US addresses do not have a foreing country code, not the USA country code
// 3 - What are INO SAF SWD?
switch ($row->Country)
{
case 'AUS': // Austria
$row->Country = "AT";
break;
case 'BEL': // Belgium
$row->Country = "BE";
break;
[more mappings]
default :
$row->Country = "US";
break;
}
}
function prepare($entity, stdClass $row){
// delete rows added by MigrateMembers
$num_deleted1 = db_delete('field_data_field_member_school')
->condition('entity_id', $this->destinationValues->entity_id)
->execute();
$num_deleted2 = db_delete('field_data_field_user_address')
->condition('entity_id', $this->destinationValues->entity_id)
->execute();
switch ($row->SchoolAffiliation)
{
case 'AR': // Arts
$row->SchoolAffiliation = "91";
break;
[more mappings]
default :
$row->SchoolAffiliation = "NULL";
break;
}
$this->destinationValues->field_member_school_tid = $row->SchoolAffiliation;
$fields = array('entity_type' => 'user', 'bundle' => 'user', 'deleted' => 0, 'entity_id' => $this->destinationValues->entity_id, 'revision_id' => $this->destinationValues->entity_id, 'language' => 'und', 'delta' => 0, 'field_user_address_country' => $row->Country, 'field_user_address_administrative_area' => $row->State, 'field_user_address_sub_administrative_area' => '', 'field_user_address_locality' => $row->City, 'field_user_address_dependent_locality' => '', 'field_user_address_postal_code' => $row->ZipCode, 'field_user_address_thoroughfare' => $row->AddressLine1, 'field_user_address_premise' => $row->AddressLine2, 'field_user_address_sub_premise' => '', 'field_user_address_organisation_name' => '', 'field_user_address_name_line' => '', 'field_user_address_first_name' => $row->FirstName, 'field_user_address_last_name' => $row->LastName, 'field_user_address_data' => '');
db_insert('field_data_field_user_address')->fields($fields)->execute();
}
}
class SchoolIDMigration extends MigrateMembers_Basic_Migration {
public function __construct($arguments) {
parent::__construct($arguments);
$this->description = 'Add data to field_data_field_SchoolID';
$this->dependencies = array('MigrateMembers');
$table_name = 'field_data_field_SchoolID';
$this->source = new MigrateSourceCSV(DRUPAL_ROOT . '/' . drupal_get_path('module', 'migrate_users') . '/data_sources/drupaluser_import.csv', $columns, array('header_rows' => 1));
$this->destination = new MigrateDestinationTable($table_name);
$this->map = new MigrateSQLMap($this->machineName,
array('Row' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
)
),
MigrateDestinationTable::getKeySchema($table_name)
);
// add uid from completed MigrateMembers migration
$this->addFieldMapping('entity_id', 'Row')->sourceMigration('MigrateMembers');
$this->addFieldMapping('revision_id', 'Row')->sourceMigration('MigrateMembers');
//this assignment does not find the SchoolID; it is set in prepare()
//$this->addFieldMapping('field_SchoolID_value', 'SchoolID')->sourceMigration('MigrateMembers');
// set some defaults from the schema
$this->addFieldMapping('delta')
->defaultValue("0");
$this->addFieldMapping('entity_type')
->defaultValue("user");
$this->addFieldMapping('bundle')
->defaultValue("user");
$this->addFieldMapping('language')
->defaultValue("und");
$this->addFieldMapping('field_SchoolID_format')
->defaultValue("plain_text");
$this->removeFieldMapping('path');
$this->removeFieldMapping('pathauto');
}
function prepare($entity, stdClass $row){
// delete rows in table added by MigrateMembers
$num_deleted = db_delete('field_data_field_SchoolID')
->condition('entity_id', $this->destinationValues->entity_id)
->execute();
//
if ($row->SchoolID) $this->destinationValues->field_SchoolID_value = $row->SchoolID;
else $this->destinationValues->field_SchoolID_value = "NULL";
}
}