A simple guide to Drupal to Drupal Migration

druplicon_migrate_6-7

Contributed Modules:

Download the contributed module and dependencies if any.

  1. Migrate : http://drupal.org/project/migrate
  2. Migrate D2D : http://drupal.org/project/migrate_d2d

Extends the Migrate framework with Migration classes.

Migration includes:

  1. Role migration
  2. Picture migration
  3. User migration
  4. File migration
  5. Taxonomy term migration
  6. Node migration
  7. Comment migration
  8. Menu migration

Step 1:
Setting up Source Database Connection holding the source Drupal installation. How to connect to multiple databases within Drupal.
Define the source DB connection In settings.php as

$databases = array (
 'default' => array (.. Destination Database ...),
 'source_connection' =>
 array (
 'default' =>
 array (
 'database' => 'your source database name',
 'username' => 'your source database user',
 'password' => 'your source database password',
 'host' => 'your source database host',
 'driver' => 'mysql',
 'prefix' => '',
 ) ));

Step 2:

Add a custom module say my_site_migration with a .info and .module files and define the following dependencies.

  • migrate
  • migrate_ui
  • migrate_d2d

If you are extending the default migration classes, use the separate files to extend classes and add in .info file as

  • files[] = user.inc
  • files[] = taxonomy.inc
  • files[] = node.inc

Step 3:
In your .module file define the following hooks:

/**
 * Implementation of hook_migrate_api().
 */
 function my_site_migration_migrate_api() {
 $api = array(
 'api' => 2,
 );
 return $api;
 }
/**
 * Implementation of hook_flush_caches().
 */
 function my_site_migration_flush_caches() {
 //define all migrations in this function
 }

Migration Classes:
Classes have names of the form Drupal{object}{version}Migration, where {object} is the type of Drupal 7 thing to create (User, Node, etc.) and {version} is the Drupal version number of the source system (5, 6, or 7). Thus, to import pictures from Drupal 6, you would use DrupalPicture6Migration.

Class Arguments:
All migrate_d2d migration classes require the following arguments:

  1. machine_name: The unique machine name to assign to this migration.
  2. source_version: The Drupal version of the source database (5, 6, or 7).
  3. description: A brief description of the migration
  4. source_connection: Connection key for the source Drupal installation.

Optional arguments that apply to all migrate_d2d migration classes:

  1. source_database: Database array as an argument to migrations. Alternative to defining source connection in settings.php.
  2. group_name: Migration group name.
  3. dependencies: Migrations that must be run before this migration.
  4. soft_dependencies: Migrations that should be run before this migration (not enforced).
  5. format_mappings: An array mapping format IDs or machine names in the source database to format machine names in the destination.
  6. source_options: An array to be passed as options to the MigrateSourceSQL constructor.
  7. version_class: Overriden helper class name.

Define the common arguments array to reuse in all migrations.

$common_arguments = array(
 'source_version' => 6,
 'source_connection' => 'source_connection'
 );

Role migrations:
Supports one additional argument

  • role_mappings: An array keyed by the source role name, with a destination role name as the value. When importing roles, instead of the source role name being created on the destination, its role ID will map to the specified destination role’s ID.
$role_arguments = $common_arguments + array(
 'machine_name' =>,
 'role_mappings' => array(
 'Admin' => 'Administrator',
 OR
 1 => 5,
 ),
 );

User migration:
Pictures should be migrated before users.

/*
 * User Picture Migration
 */
 $picture_arguments = $common_arguments + array(
 'machine_name' => 'Machine_name_of_Picture_migration',
 'description' => t('some description'),
 'default_uid' => 1, // The default owner id if owner is not present
 'source_dir' => 'path_to_source_picture_folder',
 'destination_dir' => 'path_to_destination_picture_folder',
 );</pre>
Migration::registerMigration('DrupalPicture6Migration', $picture_arguments['machine_name'], $picture_arguments);

User migration take following additional arguments:
role_migration: The machine name of your role migration class, if any.
picture_migration: The machine name of your picture migration class, if any.

$user_arguments = $common_arguments + array(
 'machine_name' => 'Machine_name_for_user_migration',
 'description' => t('Import Drupal 6 users'),
 'role_migration' => 'Machine_name_for_role_migration',
 'picture_migration' => 'Machine_name_for_picture_migration',
 );</pre>
Migration::registerMigration('DrupalUser6Migration', $user_arguments['machine_name'], $user_arguments);

File Migration:
Use the distinct file migration class, which basically map the source files (or file_managed) table to the destination file_managed table. The supported arguments are:

  1. user_migration: The machine name of your user migration, used to properly assign ownership of the files.
  2. default_uid: A default destination uid to use as the default owner of files where a legacy owner isn’t identified.
  3. source_dir: The source destination from which to copy migrated files.
  4. destination_dir: The destination to which to copy migrated files. This defaults to public://.
  5. file_class: An override class for the extended default MigrateFileUri class with application-specific behavior.
$file_arguments = $common_arguments + array(
 'machine_name' => 'Machine_name_for_file_migration',
 'description' => t('Migration of files from Drupal 6'),
 'user_migration' => 'Machine_name_for_user_migration',
 'default_uid' => 1,
 'source_dir' => 'path_to_source_file_folder',
 'destination_dir' => 'path_to_destination_file_folder',
 );</pre>
Migration::registerMigration('DrupalFile6Migration', $file_arguments['machine_name'], $file_arguments);
<pre>

Using this strategy, files are migrated before being used in a subsequent migration, such as for a node containing a referenced image field. In the (custom) node migration, the referenced file should be migrated with a field mapping specifying a file_class of MigrateFileFid. This migrates only the reference to the file, not the file itself.
Additionally, preserve_files should be set to TRUE to avoid deleting the actual file, should the node migration be rolled back.

Term Migration:
The key is mapping the source vocabulary to the destination vocabulary. The migrate class would instantiate multiple times, one for each vocabulary to migrate.

  • source_vocabulary: The unique identifier of the source vocabulary (a machine name in D7, or a vid for earlier Drupal versions).
  • destination_vocabulary: The unique machine name of the destination vocabulary.
$vocabulary_arguments = array(
 array(
 'description' => t('Migration of Tags from Drupal 6'),
 'machine_name' => 'Machine_name_of_this_vacab_migration',
 'source_vocabulary' => '1', // source vocab ID
 'destination_vocabulary' => 'Destination_Vocabulary_Machine_Name',
 ),
 array(
 'description' => t('Migration of Categories from Drupal 6'),
 'machine_name' => 'Machine_name_of_this_vacab_migration',
 'source_vocabulary' => '2', // source vocab ID
 'destination_vocabulary' => 'Destination_Vocabulary_Machine_Name',
 ),
 );

$common_vocabulary_arguments = $common_arguments + array(
 'class_name' => 'DrupalTerm6Migration'
 );

foreach ($vocabulary_arguments as $arguments) {
 $arguments += $common_vocabulary_arguments;
 Migration::registerMigration($arguments['class_name'], $arguments['machine_name'], $arguments);
 }

Node Migration:
Node migrations do mapping a source node type to a destination node type and takes the arguments.
source_type: The unique machine name of the source node type.
destination_type: The unique machine name of the destination node type.
user_migration: The machine name of your user migration, used to properly assign authorship of the nodes.
default_uid: A default destination uid to use as the default author of nodes where a source owner isn’t identified.
default_language: Default language for the node and the node body. Defaults to LANGUAGE_NONE.

$node_arguments = array(
 array(
 'machine_name' => 'Machine_name_of_content_type_1_migration',
 'description' => t('Migration of content type: nodes from Drupal 6'),
 'source_type' => 'source_content_type',
 'destination_type' => 'destination_content_type',
 'dependencies' => array('Machine_name_of_dependent_migration'), // if the dependency is defined
 ),
 array(
 'machine_name' => 'Machine_name_of_content_type_2_migration',
 'description' => t('Migration of Content Type: nodes from Drupal 6'),
 'source_type' => 'source_content_type',
 'destination_type' => 'destination_content_type',
 'dependencies' => array('Machine_name_of_dependent_migration'),
 )
 );

$common_node_arguments = $common_arguments + array(
 'user_migration' => 'Machine_name_of_user_migration',
 'default_uid' => 1,
 'class_name' => 'DrupalNode6Migration',
 );

The user_migration argument both establishes the sourceMigration() call to properly map uids, and adds the Machine_name_of_user_migration migration to the dependency list to be sure this migration runs after the user migration.

foreach ($node_arguments as $arguments) {
 $arguments = array_merge_recursive($arguments, $common_node_arguments);
 Migration::registerMigration($arguments['class_name'], $arguments['machine_name'], $arguments);
 }

Comment migration:
Comment migrations are coupled to their corresponding node migrations – for each node type that has comments, you will define a corresponding comment migration.

  1. source_type: The unique machine name of the source node type.
  2. destination_type: The unique machine name of the destination node type.
  3. user_migration: The machine name of your user migration, used to properly assign authorship of the comments.
  4. default_uid: A default destination uid to use as the default author of nodes where a legacy owner isn’t identified.
  5. node_migration: The machine name of the migration for the nodes parenting these comments.
$comment_arguments = array(
 array(
 'machine_name' => 'Machine_name_of_content_type_1_comment_migration',
 'description' => t('Migration of content type: nodes comments from Drupal 6'),
 'source_type' => 'source_content_type',
 'destination_type' => 'destination_content_type',
 'node_migration' => 'Machine_name_of_content_type_1_migration',
 ),
 array(
 'machine_name' => 'Machine_name_of_content_type_2_comment_migration',
 'description' => t('Migration of content type: nodes comments from Drupal 6'),
 'source_type' => 'source_content_type',
 'destination_type' => 'destination_content_type',
 'node_migration' => 'Machine_name_of_content_type_2_migration',
 )
 );
 $common_comment_arguments = $common_arguments + array(
 'user_migration' => 'Machine_name_of_user_migration',
 'default_uid' => 1,
 'class_name' => 'DrupalNode6Migration',
 );
 foreach ($comment_arguments as $arguments) {
 $arguments = array_merge_recursive($arguments, $common_comment_arguments);
 Migration::registerMigration($arguments['class_name'], $arguments['machine_name'], $arguments);
 }

Menu Migration:
Menu migration defines no arguments beyond the common ones. Menus themselves just have names, titles, and descriptions to migrate.

$menu_arguments = $common_arguments + array(
 'machine_name' => 'Machine_name_of_Menu_migration',
 'description' => t('Import Drupal 6 menus'),
 );</pre>
Migration::registerMigration('DrupalMenu6Migration', $menu_arguments['machine_name'], $menu_arguments);

Menu links are more complex, and require a few arguments:
menu_migration: The machine name for your menu migration, so links get assigned to the correct menu.
node_migrations: An optional array of node migrations, required in order to update nids in link_path.
term_migrations: An optional array of term migrations, required in order to update tids in link_path.

$menu_link_arguments = $common_arguments + array(
 'machine_name' => 'Machine_name_of_Menu_link_migration',
 'description' => t('Import Drupal 6 menu links'),
 'menu_migration' => 'Machine_name_of_Menu_migration',
 'node_migrations' => array('Machine_name_of_content_type_1_migration', 'Machine_name_of_content_type_2_migration'),
 'term_migrations' => array('Machine_name_of_vacab1_migration', 'Machine_name_of_vacab2_migration'),
 );
Migration::registerMigration('DrupalMenuLinks6Migration', $menu_link_arguments['machine_name'], $menu_link_arguments);

Extending the migration classes:
Standard migrate_d2d classes maps all the core fields – mapping of any custom fields will be by extending the drupal_d2d classes and adding field mappings.
Example:
Suppose in your legacy system you had field_published on your article nodes, and in the new D7 system the equivalent field is field_published_date. You can map these fields by implementing a custom class overriding the provided node class and adding the mapping:

class ExampleArticleMigration extends DrupalNode6Migration {
 public function __construct(array $arguments) {
 parent::__construct($arguments);
 $this->dependencies[] = 'ExCategories';
 // We're replacing the legacy field_published, which was a text field, with a new date field
 $this->addFieldMapping('field_published_date', 'field_published');
 //Adding a default value to a field.
 $this->addFieldMapping('domain_user')->defaultValue(array('4' => 4));
 //Defining source migration for the field value
 $this->addFieldMapping('field_avatar', 'picture')->sourceMigration('Machine_name_of_picture_migration');
 // passing arguments
 $this->addFieldMapping('field_avatar', 'picture')->sourceMigration('machine_name')->arguments(array('source_type' => 'tid'));
 // call back functions
 $this->addFieldMapping('path', 'path')->callbacks(array($this, 'callback_function'));
 }
 }

To make use of your custom class, pass its name rather than DrupalNode6Migration when registering your classes:

Migration::registerMigration('ExampleArticleMigration', $article_node_arguments['machine_name'], $article_node_arguments)

Modifying queries:
Each of the migrate_d2d classes has a query() method, returning the query object used to pull source data. We can modify the base query method in extended class to customize the query for your application:

class ExampleUserMigration extends DrupalUser6Migration {
 protected function query() {
 // Get the default query (all rows in the users table other than uid 1)
 $query = parent::query();
 // Exclude test accounts
 $query->condition('name', 'test-%', 'LIKE');
 // Add fields from a custom table
 $query->innerJoin('myextradata', 'd', 'u.uid=d.uid');
 $query->fields('d', array('favorite_food', 'favorite_song'));
 return $query;
 }
 }

Of course, you can do much more when you extend the migrate_d2d classes – add a prepareRow() implementation to manipulate the data, prepare()

Useful Migration Links:

  1. http://drupal.org/node/1813498 :: Migrate Drupal 6 data to drupal 7.
  2. http://drupal.org/migrate
  3. https://www.acquia.com/blog/drupal-drupal-data-migration-part-1-basics
  4. https://www.acquia.com/blog/drupal-drupal-data-migration-part-2-architecture

Setting up Source Database Connection in settings.php:

  1. http://drupal.org/node/18429 :: How to connect to multiple databases within Drupal
  2. http://drupal.org/node/1014558 :: Add a database connection of source database in your settings.php of destination installtion.

Hooks Used:

  1. hook_flush_cache: http://api.drupal.org/api/drupal/modules!system!system.api.php/function/hook_flush_caches/7

Run Migrations:
Enable migrate_ui module (under migrate module) and check all migration from admin/content/migrate to run your migrations. Don’t forget to flush cache after you complete your migration scripts or you have made any changes in migration classes.

Advertisements

2 thoughts on “A simple guide to Drupal to Drupal Migration

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s