0

also posted to Migrate issue queue as a support request

I am importing users and data from csv with a Migrate Group. The initial user creation and some additional data in other tables in working fine.

I have read everything I can find on Google about Migrate and Addressfield, and some of it is 2 years old and confusing. For example, Migrate has deprecated $arguments for field mappings, sub-fields are mapped with colon notation. Also, I am adding the Addressfield migration to a group so it gets the correct uid from the completed user migration, so posts about direct csv imports are not helpful.

I am getting a message when I migrate (with drush)

PHP Fatal error: Call to a member function import() on a non-object in /var/www/[site]/sites/all/modules/contrib/migrate/includes/migration.inc on line 696

I put a drush_print_r on that line

drush_print_r($this->destinationValues);drush_print_r($this->sourceValues);

the output of drush_print_r just before the error is

stdClass Object
(
    [entity_id] => 483
    [field_user_address] => Array
        (
            [0] => US
            [arguments] => Array
                (
                    [administrative_area] => NY
                    [first_name] => [actual first name]
                    [last_name] => [actual last name]
                    [locality] => New York
                    [postal_code] => 10028
                    [premise] => 
                    [thoroughfare] => [actual address]
                )

        )

    [revision_id] => 483
)

stdClass Object
(
    [Row] => 1
    [Type] => M
    [Prefix] => Miss
    [FirstName] => [actual last name]
    [MiddleName] => 
    [LastName] => [actual first name]
    [StaffNotes] => 
    [ColumbiaAffiliation] => Alumna/nus
    [ProfessionalAffiliation] => 
    [AddressLine1] => [actual address]
    [AddressLine2] => 
    [City] => New York
    [State] => NY
    [ZipCode] => 10028
    [Country] => Array
        (
            [0] => US
        )

    [TelephoneNumber] => [actual phone number]
    [Email] => [actual email]
    [Birthday] => 
    [ID] => [actual ID]
    [Descriptor] => [A] A: EN07
    [GraduationYear] => 2007
    [SchoolAffiliation] => EN
    [csvrownum] => 1
    [migrate_map_hash] => 
)

my field mappings:

$this->addFieldMapping('field_user_address', 'Country');    
$this->addFieldMapping('field_user_address:administrative_area', 'State');
$this->addFieldMapping('field_user_address:locality', 'City');
$this->addFieldMapping('field_user_address:postal_code', 'ZipCode');
$this->addFieldMapping('field_user_address:thoroughfare', 'AddressLine1');
$this->addFieldMapping('field_user_address:premise', 'AddressLine2');
$this->addFieldMapping('field_user_address:first_name', 'FirstName');
$this->addFieldMapping('field_user_address:last_name', 'LastName');

and in prepareRow():

$row->Country = array($row->Country);

1 Answer 1

0

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";
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.