How to migrate content from drupal 6 to 7 by using Migrate_d2d - Part 1

The normal way of performing a major upgrade in Drupal have traditionally been by running update.php, that fire off a lot of rather complex hook_update_N() tasks. They will try to upgrade configuration and content from one major version to another. Example Drupal 6 to 7. This is all about to change in Drupal 8. Drupal 8 have got the migrate module baked in, and upgrade is no longer a upgrade, but a migration of data and configuration from one system to another. This allow you to alter and decide what to keep and what to not. What to alter during migration and what not and many many more things. So how about migrating to Drupal 7. How difficult is it to migrate Drupal 6 to 7?

Migrating data a cross systems have never been easy, and the documentation is normally hard to grasp if you are new to migrate and migrate_d2d. In a few blog post will I try to break this down into easier and more understandable bits. These articles will probable never answer all your questions, but it will get you going. I will try to get you out of that dark pit, where nothing works and you have no idea why. You do not even know what to ask or search for. We have all been there.

Updates

  • 27.05.2015 - Commit 809624aae alter the way migrate classes are autoloaded.
  • 27.05.2015 - Included troubleshooting registering your classes.

Blog post in this series

Stuff you might need

Documentation

  • Migrate D2d documentation https://www.drupal.org/node/1813498
  • Migrate https://www.drupal.org/migrate

Modules needed by Drupal 7

  • https://www.drupal.org/project/migrate_d2d
  • https://www.drupal.org/project/migrate

Tools you must have and know how to use

  • Working Git installation http://www.git-scm.com/
  • drush https://github.com/drush-ops/drush

Code

All used will be committed to my github. Much of this code build upon the provided example code that ships with Migrate and Migrate_d2d.

Test setup

General overview Overview of what how everything is wired up. All running code live in Drupal 7. We only need access to the Drupal 6 database and file.

Drupal 7 "sees" both databases and have access to the file system or directory where Drupal 6 have stored it's files. We will demonstrate migration of a standard Drupal 6 installation. I will demonstrate through the series how to import: users, taxonomy terms, files and nodes, to a Drupal 7 site.

Drupal 6 setup

Run a clean installation of Drupal 6 and download the following modules: devel, cck and filefield.

drush dl devel cck filefield
drush en -y  filefield devel devel_generate content text

Goto admin/content/node-type/story/fields and add the following fields to the story content types:

  • file field field_file
  • text field field_foo

Drupal 6 story field settings

Drupal 7 setup

Drupal standard installation profile and then enable migrate, migrate_d2d and devel (always nice to have).

drush dl migrate migrate_d2d devel
drush en devel_generate devel migrate_d2d_ui migrate_d2d migrate migrate_ui

Drupal 7 article field settings

Go to admin/structure/types/manage/article/fields and add:

  • file field (field_file)
  • text field (field_bar)

Create content in Drupal 6

We Also need content to migrate. The easiest way of doing this by using devel_generate. We need to add: users, taxonomy and content for both story and page content type.

Goto /admin/generate. Make sure you create content that also populate the two custom fields we just created, and create taxonomies.

Drupal 7 - settings.php

You need to alter the settings.php of your Drupal 7 instance to enable your Drupal 7 instance to read your Drupal 6 database.

$databases = array (
  'default' =>
  array (
    'default' =>
    array (
      'database' => 'smbd7',
      'username' => 'test',
      'password' => 'test',
      'host' => 'localhost',
      'port' => '',
      'driver' => 'mysql',
      'prefix' => '',
    ),
  ),
  'legacy' => array(
    'default' => array(
      'database' => 'smbd6',
      'username' => 'test',
      'password' => 'test',
      'host' => 'localhost',
      'port' => '',
      'driver' => 'mysql',
      'prefix' => '',
    ),
  ),
);

You will need to alter the database name, user name and passwords to match your test setup. You can now test and make sure that Drupal 7 is able to connect and access your Drupal 6 database:

drush sql-cli --database=legacy

Exampe 1 Migrate user accounts

This is some of the most basic things you can do and require very little code. Grab the code from github and clone it into your Drupal 7 sites/all/modules/ directory.

git clone --branch example1 https://github.com/steinmb/smb_migrate.git

Register and de-register statically defined classes

After the module is enabled does Migrate require you might have to register your migration task (class) (Introduced by Migrate 7.x-2.6). Running drush ms should list your taxonomy migration tasks if not you have to register them by running the following drush command:

drush migrate-register

You should see something like "All statically defined migrations have been (re)registered". You can also use the UI by visiting admin/content/migrate/configure and press the button labeled "Register statically defined classes". This should take you to the migration overview page that should list this migration.

You can also remove registered import tasks and groups with migrate-deregister

drush migrate-deregister --help

lists available options. Notice this is also how you remove orphans. They are normally migrations groups and tasks left behind after you alter and/or remove migration tasks during development.

Problems registering your migration classes?

Some users seems to have problem registering the migration tasks/classes. There is a few things to check if so:

  1. Make sure you remembered to use hook_migrate_api(). Replace hook with the machine name of your migration module.
  2. Name your include files correctly. The file that contain hook_migrate_api() is normally named module_machine-name.migrate.inc>.
  3. Make sure module_machine-name.migrate.inc is included in your module info file to allow drupal autoloader to do it's thing.

Migration status

Migrate come with drush command that quickly will become your new best friend, migrate status:

drush ms

Should return something like this:

Group: content_migration  Total  Imported  Unprocessed  Status  Last imported
User                      51     0         51           Idle

Migrate found one group (content_migration) that contain a migration named Users, 51 users that never have been migrated.

Migrate users

Let's go ahead and migrate all exiting user accounts from Drupal 6. Note: It will not migrate user 1.

drush mi User

This should give something along these lines:

Processed 51 (50 created, 0 updated, 0 failed, 1 ignored) in 0.7 sec (4423/min) - done with 'User'

It imported all users, created them, ignored user 1, speed was 4423 user pr. minute.

Have another look at migrate status (ms):

drush ms
Group: content_migration  Total  Imported  Unprocessed  Status  Last imported
User                      51     50        0            Idle    2015-05-12 10:10:23

It will also show up in migrate UI at admin/content/migrate

Revert migration, or roll back migrated content

You can anytime remove (revert) imported users and content by running migrate revert (mr). This is one of the most useful features of migrate, allowing you to roll back content, alter the migration and run it again. Lather, rinse, repeat.

drush mr User

Code breakdown

As you see, not much code but let's have a look at what we currently have.

smb_migrate.info

name = "SMB migration"
package = "Migration"
core = 7.x

dependencies[] = migrate
dependencies[] = migrate_d2d

files[] = smb_migrate.migrate.inc

Have you ever written an module in Drupal will you find nothing special here except files[] = smb_migrate.migrate.inc that is important. Omit this, and your code will never get loaded by the Drupal autoloader.

smb_migrate.module

<?php
/**
 * @file smb_migrate.module
 *
 */
?>

Even more boring. Empty module file. Normally does this not contain any migration code.

smb_migrate.migrate.inc

This is where the import tasks (class) is defined, and in this example, contains the only interesting bits of code.

<?php
/**
 * Implements hook_migrate_api().
 */
function smb_migrate_migrate_api() {
 
$api = array(
   
'api' => 2,
   
'groups' => array(
     
'content_migration' => array(
       
'title' => t('SMB Drupal 6 to Drupal 7 migration example.'),
      ),
    ),
  );

 
// Specify the DB connection and the source Drupal version.
 
$common_arguments = array(
   
'source_connection' => 'legacy',
   
'source_version' => 6,
   
'group_name' => 'content_migration',
  );

 
/**
   * Register the user migration.
   * We just use the migrate_d2d D6 migration class as-is.
   */
 
$api['migrations']['User'] = $common_arguments + array(
   
'description' => t('Migration of users from Drupal 6'),
   
'class_name' => 'DrupalUser6Migration',
  );

  return
$api;
}
?>

'groups' => array(
  'content_migration' => array(
    'title' => t('SMB Drupal 6 to Drupal 7 migration example.'),

You can create groups that groups migration task. Allowing you to with a single run, fire off multiple migrations.

$common_arguments = array(
  'source_connection' => 'legacy',
  'source_version' => 6,
  'group_name' => 'content_migration',

This is just that, common settings (arguments). This well be added to your migration else if you explicit define this in your migration.

$api['migrations']['User'] = $common_arguments + array(
  'description' => t('Migration of users from Drupal 6'),
  'class_name' => 'DrupalUser6Migration',

This is where the migration 'User' get defined. 'class_name' is special. This define the name of the PHP class that are doing the heavy lifting during the migration. This class extend the common migration class coming from Migrate (DrupalUserMigration). In another blog post will I show how to implement your own class where you can perform your own magic.

Blog post in this series

Comments

Related, you might be interested in Code Server - https://www.drupal.org/project/codeserver

Add new comment