The Digital Mayflower 
Data Pilgrimage with the Migrate Module 
1
Erich Beyrent 
Working with Drupal 
since 2004! 
Drupal: http://drupal.org/u/ebeyrent 
Twitter: http://twitter.com/ebeyrent 
Github: http://github.com/ebeyrent 
2
Agenda 
3
Agenda 
• Prerequisites 
4
Agenda 
• Prerequisites 
• Migrate Concepts 
5
Agenda 
• Prerequisites 
• Migrate Concepts 
• Implementation 
6
Agenda 
• Prerequisites 
• Migrate Concepts 
• Implementation 
• Classes & Organization 
7
Agenda 
• Prerequisites 
• Migrate Concepts 
• Implementation 
• Classes & Organization 
• Execution 
8
Agenda 
• Prerequisites 
• Migrate Concepts 
• Implementation 
• Classes & Organization 
• Execution 
• Performance 
9
Agenda 
• Prerequisites 
• Migrate Concepts 
• Implementation 
• Classes & Organization 
• Execution 
• Performance 
• Q & A 
10
11
12
13
14
Prerequisites 
• A working Drupal 7 / Drupal 8 installation 
• Basic understanding of PHP OO 
• Understanding of Drupal modules, hooks, 
and the API 
• Understanding of how to write SQL 
and the QueryInterface 
15
16
Migrate Goals 
17 
• Accurate 
• Repeatable 
• Performant 
• Safe
Concepts 
Source 
The source is the interface to the data that is 
being migrated. 
18
Concepts 
Destination 
Destinations are responsible for saving data in 
Drupal. 
19
Concepts 
Map 
Connects the source to the destination 
20
Implementation 
The Module 
Implements hook_migrate_api() 
Registers classes in the .info file 
21
; ** 
; MY_MODULE.info 
; ** 
dependencies[] = migrate 
files[] = migrate_newsletters.module 
files[] = migrate_newsletters.migrate.inc 
files[] = base.inc 
files[] = newsletters.inc 
files[] = subscribers.inc 
files[] = sources.inc 
files[] = subscriptions.inc 
22
23 
/** 
* Implements hook_migrate_api(). 
*/ 
function MY_MODULE_migrate_api() { 
$api = array( 
'api' => 2, 
'groups' => array( 
'newsletters' => array( 
'title' => t('Newsletter'), 
), 
), 
'field handlers' => array( 
'DateMigrateFieldHandler', 
), 
'migrations' => array( 
'Newsletters' => array( 
'class_name' => 'NewslettersMigration', 
'group_name' => 'newsletters', 
), 
'Sources' => array( 
'class_name' => 'SourcesMigration', 
'group_name' => 'newsletters', 
), 
), 
); 
return $api; 
}
24 
/** 
* Implements hook_migrate_api(). 
*/ 
function MY_MODULE_migrate_api() { 
$api = array( 
'api' => 2, 
'groups' => array( 
'newsletters' => array( 
'title' => t('Newsletter'), 
), 
), 
'field handlers' => array( 
'DateMigrateFieldHandler', 
), 
'migrations' => array( 
'Newsletters' => array( 
'class_name' => 'NewslettersMigration', 
'group_name' => 'newsletters', 
), 
'Sources' => array( 
'class_name' => 'SourcesMigration', 
'group_name' => 'newsletters', 
), 
), 
); 
return $api; 
}
25 
/** 
* Implements hook_migrate_api(). 
*/ 
function MY_MODULE_migrate_api() { 
$api = array( 
'api' => 2, 
'groups' => array( 
'newsletters' => array( 
'title' => t('Newsletter'), 
), 
), 
'field handlers' => array( 
'DateMigrateFieldHandler', 
), 
'migrations' => array( 
'Newsletters' => array( 
'class_name' => 'NewslettersMigration', 
'group_name' => 'newsletters', 
), 
'Sources' => array( 
'class_name' => 'SourcesMigration', 
'group_name' => 'newsletters', 
), 
), 
); 
return $api; 
}
26 
/** 
* Implements hook_migrate_api(). 
*/ 
function MY_MODULE_migrate_api() { 
$api = array( 
'api' => 2, 
'groups' => array( 
'newsletters' => array( 
'title' => t('Newsletter'), 
), 
), 
'field handlers' => array( 
'DateMigrateFieldHandler', 
), 
'migrations' => array( 
'Newsletters' => array( 
'class_name' => 'NewslettersMigration', 
'group_name' => 'newsletters', 
), 
'Sources' => array( 
'class_name' => 'SourcesMigration', 
'group_name' => 'newsletters', 
), 
), 
); 
return $api; 
}
27 
/** 
* Implements hook_migrate_api(). 
*/ 
function MY_MODULE_migrate_api() { 
$api = array( 
'api' => 2, 
'groups' => array( 
'newsletters' => array( 
'title' => t('Newsletter'), 
), 
), 
'field handlers' => array( 
'DateMigrateFieldHandler', 
), 
'migrations' => array( 
'Newsletters' => array( 
'class_name' => 'NewslettersMigration', 
'group_name' => 'newsletters', 
), 
'Sources' => array( 
'class_name' => 'SourcesMigration', 
'group_name' => 'newsletters', 
), 
), 
); 
return $api; 
}
Implementation 
The Classes 
Migration classes extend the abstract 
Migration class 
28
abstract class NewslettersBaseMigration extends Migration { 
/** 
* @var array 
*/ 
protected $sourceConfiguration = array(); 
/** 
* Class constructor. 
*/ 
public function __construct($group = NULL) { 
// Always call the parent constructor first for basic setup. 
parent::__construct($group); 
$this->team = array( 
new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), 
); 
$this->issuePattern = 'https://example.com/issues/:id:'; 
$this->sourceConfiguration = array( 
'servername' => 'localhost', 
'username' => 'admin', 
'password' => 'bubbles', 
'database' => ‘source_database', 
); 
} 
} 
29
abstract class NewslettersBaseMigration extends Migration { 
/** 
* @var array 
*/ 
protected $sourceConfiguration = array(); 
/** 
* Class constructor. 
*/ 
public function __construct($group = NULL) { 
// Always call the parent constructor first for basic setup. 
parent::__construct($group); 
$this->team = array( 
new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), 
); 
$this->issuePattern = 'https://example.com/issues/:id:'; 
$this->sourceConfiguration = array( 
'servername' => 'localhost', 
'username' => 'admin', 
'password' => 'bubbles', 
'database' => ‘source_database', 
); 
} 
} 
30
abstract class NewslettersBaseMigration extends Migration { 
/** 
* @var array 
*/ 
protected $sourceConfiguration = array(); 
/** 
* Class constructor. 
*/ 
public function __construct($group = NULL) { 
// Always call the parent constructor first for basic setup. 
parent::__construct($group); 
$this->team = array( 
new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), 
); 
$this->issuePattern = 'https://example.com/issues/:id:'; 
$this->sourceConfiguration = array( 
'servername' => 'localhost', 
'username' => 'admin', 
'password' => 'bubbles', 
'database' => ‘source_database', 
); 
} 
} 
31
abstract class NewslettersBaseMigration extends Migration { 
/** 
* @var array 
*/ 
protected $sourceConfiguration = array(); 
/** 
* Class constructor. 
*/ 
public function __construct($group = NULL) { 
// Always call the parent constructor first for basic setup. 
parent::__construct($group); 
$this->team = array( 
new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), 
); 
$this->issuePattern = 'https://example.com/issues/:id:'; 
$this->sourceConfiguration = array( 
'servername' => 'localhost', 
'username' => 'admin', 
'password' => 'bubbles', 
'database' => ‘source_database', 
); 
} 
} 
32
Anatomy of a 
migration class 
Migration classes typically contain four methods: 
• __construct() 
• prepare() 
• prepareRow() 
• complete() 
33
class NewslettersMigration extends NewslettersBaseMigration { 
/** 
* Class constructor method. 
* 
* @param array $arguments 
*/ 
public function __construct(array $arguments = array()) { 
parent::__construct($arguments); 
$this->description = t(‘Newsletters’); 
$options = array( 
34 
'track_changes' => 1 
);
class NewslettersMigration extends NewslettersBaseMigration { 
/** 
* Class constructor method. 
* 
* @param array $arguments 
*/ 
public function __construct(array $arguments = array()) { 
parent::__construct($arguments); 
$this->description = t(‘Newsletters’); 
$options = array( 
35 
'track_changes' => 1 
);
// List of source fields to import. 
$source_fields = array( 
‘email’ => t(‘Subscriber email address’), 
// … 
); 
$select_query = Database::getConnection('default', ‘legacy') 
->select('profile', 'p'); 
$select_query->fields('p', array()); 
$select_query->orderBy(‘email’); 
$count_query = Database::getConnection('default', ‘legacy') 
->select('profile', 'p'); 
$count_query->fields('p', array()); 
$count_query->orderBy(‘email’); 
$count_query->addExpression(‘COUNT(email)’, ‘count’); 
$this->source = new MigrateSourceSQL( 
$select_query, 
$source_fields, 
$count_query, 
$options 
); 
36
$this->destination = new MigrateDestinationTable(‘subscribers’); 
/* 
* Other examples of destination: 
* 
* $this->destination = new MigrateDestinationNode(‘newsletter'); 
* 
* $this->destination = new MigrateDestinationTerm(‘advertising_source'); 
* 
*/ 
37
// Source and destination relation for rollbacks. 
$this->map = new MigrateSQLMap( 
$this->machineName, 
array( 
'id' => array( 
'type' => 'int', 
'not null' => TRUE, 
'description' => 'Newsletter ID.', 
'alias' => 'n', 
) 
), 
MigrateDestinationNode::getKeySchema() 
); 
38
// Source and destination relation for rollbacks. 
$this->map = new MigrateSQLMap( 
$this->machineName, 
array( 
'member_id' => array( 
'type' => 'varchar', 
'length' => 128, 
'not null' => TRUE, 
'description' => 'Subscriber Email ID.', 
'alias' => 'member_id', 
), 
), 
MigrateDestinationTable::getKeySchema('subscribers') 
); 
39
// Source and destination relation for rollbacks. 
$this->map = new MigrateSQLMap( 
$this->machineName, 
array( 
'Source_ID' => array( 
'type' => 'int', 
'not null' => TRUE, 
'description' => 'Source ID.', 
'alias' => 't', 
), 
), 
MigrateDestinationTerm::getKeySchema() 
); 
40
// Basic field mapping. 
$this->addFieldMapping('email', 'email') 
->description(t('Map the subscriber email from the source’)); 
// More advanced field mapping. 
$this->addFieldMapping('uuid', 'uuid') 
->description(t('UUID for the subscriber’)) 
->defaultValue(uuid_generate()) 
->issueGroup(t('Client questions')) 
->issueNumber(765736) 
->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_OK); 
// Getting fancy - convert string to an array. 
$this->addFieldMapping(‘field_tags', ‘tags') 
->separator(‘,’); 
// Process the field with a callback. 
$this->addFieldMapping(‘json_field’, ‘json’) 
->callback(‘json_decode’); 
// De-duplicate data. 
$this->addFieldMapping('name', 'username') 
->dedupe('users', 'name'); 
41
Map fields to previously migrated fields 
// Map the field to a value from another migration. 
$this->addFieldMapping('uid', 'author_id') 
42 
->sourceMigration('Users');
Skip source and destination fields 
43 
// Unmapped source fields. 
$this->addUnmigratedSources( 
array( 
'first_name', 
'last_name', 
) 
); 
// Unmapped destination fields. 
$this->addUnmigratedDestinations( 
array( 
'eid', 
) 
);
Define the highwater field 
44 
// Define the highwater field. 
$this->highwaterField = array( 
'name' => 'last_changed', 
'alias' => 'w', 
'type' => 'int', 
);
Anatomy of a 
migration class 
• prepare($entity, stdClass $row) 
This method is called just before saving the 
entity. 
• complete($entity, stdClass $row) 
This method is called immediately after the 
entity is saved. 
45
Anatomy of a 
migration class 
• prepareRow(stdClass $row) 
This method is for conditionally skipping rows, 
and for modifying fields before other handlers 
• createStub($migration, array $id) 
This method is for creating stub objects to be 
referenced by the current row 
46
Anatomy of a 
migration class 
source: http://alexrayu.com/blog/drupal-migration-onion-skin-or-node 
47
Migration flow 
Iterator prepareRow() 
Mappings & 
field handlers 
prepare() 
complete() 
48
Execution 
Use drush, not the web interface 
https://www.drupal.org/node/1806824 
Run a single migration: 
drush migrate-import Subscribers 
Run all the migrations in a migration group: 
drush migrate-import --group newsletters 
49
Performance 
Benchmarking 
$ drush mi Migration --instrument [timer, memory, all] 
$ drush mi Newsletters  
--limit="100 items"  
--instrument=“all"  
--feedback=“30 seconds" 
50
Performance 
Database indexes add a lot of overhead on 
writes, especially with multiple multi-column 
indexes 
db_drop_index($table, $index) 
db_add_index($table, $index, array $fields) 
51
class MyMigration extends MigrationBase { 
/** 
* Code to execute before first article row is imported. 
*/ 
public function preImport() { 
parent::preImport(); 
$this->dropIndexes(); 
} 
/** 
* Code to execute after the last article row has been imported. 
*/ 
public function postImport() { 
parent::postImport(); 
$this->restoreIndexes(); 
} 
protected function dropIndexes() { 
db_drop_index('my_table', 'name_of_index'); 
return $this; 
} 
protected function restoreIndexes() { 
db_add_index('my_table', 'name_of_index', array('name_of_field')); 
return $this; 
} 
} 
52
Performance 
53 
MySQL Tuning 
innodb_flush_log_at_trx_commit = 0 
innodb_doublewrite = 0 
innodb_support_xa = 0
Performance 
MySQL Tuning 
key_buffer_size = 128M 
max_allowed_packet = 20M 
query_cache_size = 128M 
table_open_cache = 64 
read_buffer_size = 2M 
read_rnd_buffer_size = 8M 
myisam_sort_buffer_size = 16M 
join_buffer_size = 4M 
tmp_table_size = 92M 
max_heap_table_size = 92M 
sort_buffer_size = 4M 
innodb_additional_mem_pool_size = 8M 
54
Performance 
55 
MySQL Tuning 
innodb_file_per_table
Resources 
Project page - http://drupal.org/project/migrate 
Migrate Handbook - http://drupal.org/node/415260 
Drush migrate commands - http://drupal.org/node/1561820 
Using stubs - http://drupal.org/node/1013506 
Database Tuning - http://www.drupal.org/node/1994584 
56
Questions & Answers 
57

Digital Mayflower - Data Pilgrimage with the Drupal Migrate Module

  • 1.
    The Digital Mayflower Data Pilgrimage with the Migrate Module 1
  • 2.
    Erich Beyrent Workingwith Drupal since 2004! Drupal: http://drupal.org/u/ebeyrent Twitter: http://twitter.com/ebeyrent Github: http://github.com/ebeyrent 2
  • 3.
  • 4.
  • 5.
    Agenda • Prerequisites • Migrate Concepts 5
  • 6.
    Agenda • Prerequisites • Migrate Concepts • Implementation 6
  • 7.
    Agenda • Prerequisites • Migrate Concepts • Implementation • Classes & Organization 7
  • 8.
    Agenda • Prerequisites • Migrate Concepts • Implementation • Classes & Organization • Execution 8
  • 9.
    Agenda • Prerequisites • Migrate Concepts • Implementation • Classes & Organization • Execution • Performance 9
  • 10.
    Agenda • Prerequisites • Migrate Concepts • Implementation • Classes & Organization • Execution • Performance • Q & A 10
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
    Prerequisites • Aworking Drupal 7 / Drupal 8 installation • Basic understanding of PHP OO • Understanding of Drupal modules, hooks, and the API • Understanding of how to write SQL and the QueryInterface 15
  • 16.
  • 17.
    Migrate Goals 17 • Accurate • Repeatable • Performant • Safe
  • 18.
    Concepts Source Thesource is the interface to the data that is being migrated. 18
  • 19.
    Concepts Destination Destinationsare responsible for saving data in Drupal. 19
  • 20.
    Concepts Map Connectsthe source to the destination 20
  • 21.
    Implementation The Module Implements hook_migrate_api() Registers classes in the .info file 21
  • 22.
    ; ** ;MY_MODULE.info ; ** dependencies[] = migrate files[] = migrate_newsletters.module files[] = migrate_newsletters.migrate.inc files[] = base.inc files[] = newsletters.inc files[] = subscribers.inc files[] = sources.inc files[] = subscriptions.inc 22
  • 23.
    23 /** *Implements hook_migrate_api(). */ function MY_MODULE_migrate_api() { $api = array( 'api' => 2, 'groups' => array( 'newsletters' => array( 'title' => t('Newsletter'), ), ), 'field handlers' => array( 'DateMigrateFieldHandler', ), 'migrations' => array( 'Newsletters' => array( 'class_name' => 'NewslettersMigration', 'group_name' => 'newsletters', ), 'Sources' => array( 'class_name' => 'SourcesMigration', 'group_name' => 'newsletters', ), ), ); return $api; }
  • 24.
    24 /** *Implements hook_migrate_api(). */ function MY_MODULE_migrate_api() { $api = array( 'api' => 2, 'groups' => array( 'newsletters' => array( 'title' => t('Newsletter'), ), ), 'field handlers' => array( 'DateMigrateFieldHandler', ), 'migrations' => array( 'Newsletters' => array( 'class_name' => 'NewslettersMigration', 'group_name' => 'newsletters', ), 'Sources' => array( 'class_name' => 'SourcesMigration', 'group_name' => 'newsletters', ), ), ); return $api; }
  • 25.
    25 /** *Implements hook_migrate_api(). */ function MY_MODULE_migrate_api() { $api = array( 'api' => 2, 'groups' => array( 'newsletters' => array( 'title' => t('Newsletter'), ), ), 'field handlers' => array( 'DateMigrateFieldHandler', ), 'migrations' => array( 'Newsletters' => array( 'class_name' => 'NewslettersMigration', 'group_name' => 'newsletters', ), 'Sources' => array( 'class_name' => 'SourcesMigration', 'group_name' => 'newsletters', ), ), ); return $api; }
  • 26.
    26 /** *Implements hook_migrate_api(). */ function MY_MODULE_migrate_api() { $api = array( 'api' => 2, 'groups' => array( 'newsletters' => array( 'title' => t('Newsletter'), ), ), 'field handlers' => array( 'DateMigrateFieldHandler', ), 'migrations' => array( 'Newsletters' => array( 'class_name' => 'NewslettersMigration', 'group_name' => 'newsletters', ), 'Sources' => array( 'class_name' => 'SourcesMigration', 'group_name' => 'newsletters', ), ), ); return $api; }
  • 27.
    27 /** *Implements hook_migrate_api(). */ function MY_MODULE_migrate_api() { $api = array( 'api' => 2, 'groups' => array( 'newsletters' => array( 'title' => t('Newsletter'), ), ), 'field handlers' => array( 'DateMigrateFieldHandler', ), 'migrations' => array( 'Newsletters' => array( 'class_name' => 'NewslettersMigration', 'group_name' => 'newsletters', ), 'Sources' => array( 'class_name' => 'SourcesMigration', 'group_name' => 'newsletters', ), ), ); return $api; }
  • 28.
    Implementation The Classes Migration classes extend the abstract Migration class 28
  • 29.
    abstract class NewslettersBaseMigrationextends Migration { /** * @var array */ protected $sourceConfiguration = array(); /** * Class constructor. */ public function __construct($group = NULL) { // Always call the parent constructor first for basic setup. parent::__construct($group); $this->team = array( new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), ); $this->issuePattern = 'https://example.com/issues/:id:'; $this->sourceConfiguration = array( 'servername' => 'localhost', 'username' => 'admin', 'password' => 'bubbles', 'database' => ‘source_database', ); } } 29
  • 30.
    abstract class NewslettersBaseMigrationextends Migration { /** * @var array */ protected $sourceConfiguration = array(); /** * Class constructor. */ public function __construct($group = NULL) { // Always call the parent constructor first for basic setup. parent::__construct($group); $this->team = array( new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), ); $this->issuePattern = 'https://example.com/issues/:id:'; $this->sourceConfiguration = array( 'servername' => 'localhost', 'username' => 'admin', 'password' => 'bubbles', 'database' => ‘source_database', ); } } 30
  • 31.
    abstract class NewslettersBaseMigrationextends Migration { /** * @var array */ protected $sourceConfiguration = array(); /** * Class constructor. */ public function __construct($group = NULL) { // Always call the parent constructor first for basic setup. parent::__construct($group); $this->team = array( new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), ); $this->issuePattern = 'https://example.com/issues/:id:'; $this->sourceConfiguration = array( 'servername' => 'localhost', 'username' => 'admin', 'password' => 'bubbles', 'database' => ‘source_database', ); } } 31
  • 32.
    abstract class NewslettersBaseMigrationextends Migration { /** * @var array */ protected $sourceConfiguration = array(); /** * Class constructor. */ public function __construct($group = NULL) { // Always call the parent constructor first for basic setup. parent::__construct($group); $this->team = array( new MigrateTeamMember(‘Joe Migrate', ‘foo@bar.com', t('admin')), ); $this->issuePattern = 'https://example.com/issues/:id:'; $this->sourceConfiguration = array( 'servername' => 'localhost', 'username' => 'admin', 'password' => 'bubbles', 'database' => ‘source_database', ); } } 32
  • 33.
    Anatomy of a migration class Migration classes typically contain four methods: • __construct() • prepare() • prepareRow() • complete() 33
  • 34.
    class NewslettersMigration extendsNewslettersBaseMigration { /** * Class constructor method. * * @param array $arguments */ public function __construct(array $arguments = array()) { parent::__construct($arguments); $this->description = t(‘Newsletters’); $options = array( 34 'track_changes' => 1 );
  • 35.
    class NewslettersMigration extendsNewslettersBaseMigration { /** * Class constructor method. * * @param array $arguments */ public function __construct(array $arguments = array()) { parent::__construct($arguments); $this->description = t(‘Newsletters’); $options = array( 35 'track_changes' => 1 );
  • 36.
    // List ofsource fields to import. $source_fields = array( ‘email’ => t(‘Subscriber email address’), // … ); $select_query = Database::getConnection('default', ‘legacy') ->select('profile', 'p'); $select_query->fields('p', array()); $select_query->orderBy(‘email’); $count_query = Database::getConnection('default', ‘legacy') ->select('profile', 'p'); $count_query->fields('p', array()); $count_query->orderBy(‘email’); $count_query->addExpression(‘COUNT(email)’, ‘count’); $this->source = new MigrateSourceSQL( $select_query, $source_fields, $count_query, $options ); 36
  • 37.
    $this->destination = newMigrateDestinationTable(‘subscribers’); /* * Other examples of destination: * * $this->destination = new MigrateDestinationNode(‘newsletter'); * * $this->destination = new MigrateDestinationTerm(‘advertising_source'); * */ 37
  • 38.
    // Source anddestination relation for rollbacks. $this->map = new MigrateSQLMap( $this->machineName, array( 'id' => array( 'type' => 'int', 'not null' => TRUE, 'description' => 'Newsletter ID.', 'alias' => 'n', ) ), MigrateDestinationNode::getKeySchema() ); 38
  • 39.
    // Source anddestination relation for rollbacks. $this->map = new MigrateSQLMap( $this->machineName, array( 'member_id' => array( 'type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'description' => 'Subscriber Email ID.', 'alias' => 'member_id', ), ), MigrateDestinationTable::getKeySchema('subscribers') ); 39
  • 40.
    // Source anddestination relation for rollbacks. $this->map = new MigrateSQLMap( $this->machineName, array( 'Source_ID' => array( 'type' => 'int', 'not null' => TRUE, 'description' => 'Source ID.', 'alias' => 't', ), ), MigrateDestinationTerm::getKeySchema() ); 40
  • 41.
    // Basic fieldmapping. $this->addFieldMapping('email', 'email') ->description(t('Map the subscriber email from the source’)); // More advanced field mapping. $this->addFieldMapping('uuid', 'uuid') ->description(t('UUID for the subscriber’)) ->defaultValue(uuid_generate()) ->issueGroup(t('Client questions')) ->issueNumber(765736) ->issuePriority(MigrateFieldMapping::ISSUE_PRIORITY_OK); // Getting fancy - convert string to an array. $this->addFieldMapping(‘field_tags', ‘tags') ->separator(‘,’); // Process the field with a callback. $this->addFieldMapping(‘json_field’, ‘json’) ->callback(‘json_decode’); // De-duplicate data. $this->addFieldMapping('name', 'username') ->dedupe('users', 'name'); 41
  • 42.
    Map fields topreviously migrated fields // Map the field to a value from another migration. $this->addFieldMapping('uid', 'author_id') 42 ->sourceMigration('Users');
  • 43.
    Skip source anddestination fields 43 // Unmapped source fields. $this->addUnmigratedSources( array( 'first_name', 'last_name', ) ); // Unmapped destination fields. $this->addUnmigratedDestinations( array( 'eid', ) );
  • 44.
    Define the highwaterfield 44 // Define the highwater field. $this->highwaterField = array( 'name' => 'last_changed', 'alias' => 'w', 'type' => 'int', );
  • 45.
    Anatomy of a migration class • prepare($entity, stdClass $row) This method is called just before saving the entity. • complete($entity, stdClass $row) This method is called immediately after the entity is saved. 45
  • 46.
    Anatomy of a migration class • prepareRow(stdClass $row) This method is for conditionally skipping rows, and for modifying fields before other handlers • createStub($migration, array $id) This method is for creating stub objects to be referenced by the current row 46
  • 47.
    Anatomy of a migration class source: http://alexrayu.com/blog/drupal-migration-onion-skin-or-node 47
  • 48.
    Migration flow IteratorprepareRow() Mappings & field handlers prepare() complete() 48
  • 49.
    Execution Use drush,not the web interface https://www.drupal.org/node/1806824 Run a single migration: drush migrate-import Subscribers Run all the migrations in a migration group: drush migrate-import --group newsletters 49
  • 50.
    Performance Benchmarking $drush mi Migration --instrument [timer, memory, all] $ drush mi Newsletters --limit="100 items" --instrument=“all" --feedback=“30 seconds" 50
  • 51.
    Performance Database indexesadd a lot of overhead on writes, especially with multiple multi-column indexes db_drop_index($table, $index) db_add_index($table, $index, array $fields) 51
  • 52.
    class MyMigration extendsMigrationBase { /** * Code to execute before first article row is imported. */ public function preImport() { parent::preImport(); $this->dropIndexes(); } /** * Code to execute after the last article row has been imported. */ public function postImport() { parent::postImport(); $this->restoreIndexes(); } protected function dropIndexes() { db_drop_index('my_table', 'name_of_index'); return $this; } protected function restoreIndexes() { db_add_index('my_table', 'name_of_index', array('name_of_field')); return $this; } } 52
  • 53.
    Performance 53 MySQLTuning innodb_flush_log_at_trx_commit = 0 innodb_doublewrite = 0 innodb_support_xa = 0
  • 54.
    Performance MySQL Tuning key_buffer_size = 128M max_allowed_packet = 20M query_cache_size = 128M table_open_cache = 64 read_buffer_size = 2M read_rnd_buffer_size = 8M myisam_sort_buffer_size = 16M join_buffer_size = 4M tmp_table_size = 92M max_heap_table_size = 92M sort_buffer_size = 4M innodb_additional_mem_pool_size = 8M 54
  • 55.
    Performance 55 MySQLTuning innodb_file_per_table
  • 56.
    Resources Project page- http://drupal.org/project/migrate Migrate Handbook - http://drupal.org/node/415260 Drush migrate commands - http://drupal.org/node/1561820 Using stubs - http://drupal.org/node/1013506 Database Tuning - http://www.drupal.org/node/1994584 56
  • 57.