Last updated June 18, 2013. Created on May 23, 2013.
Edited by capynet, wusel, mikeryan. Log in to edit this page.

Migrate 2.6 introduces an API to assist in the development of step-by-step wizards for registering migration processes. By implementing a class extending MigrateUIWizard, and functions providing forms for each step of your wizard, you can easily provide a simple UI for importing into Drupal from your chosen source.

Registering your class

The MigrateUIWizard class is defined by the migrate_ui module - add it as a dependency for your module in your .info file.

In hook_migrate_api(), list any classes you are defining as wizard classes (derived from MigrateUIWizard). For example:

function example_migrate_api() {
$api = array(
'api' => 2,
'wizard classes' => array('ExampleMigrateWizard'),

Best practice is to define both hook_migrate_api() and your wizard class in Note that this file will need to be added to files[] in your .info file.

dependencies[] = migrate_ui
files[] =

Extending MigrateUIWizard

All the rest of the work will be in your custom class, derived from MigrateUIWizard:

class ExampleMigrateWizard extends MigrateUIWizard {

In your constructor, you will define the steps of your wizard, in the order they will appear, using addStep(). The first argument is the title that will be used for that page of the wizard, and the second is the name of the method (function) that defines the form for that page.:

public function __construct() {
$this->addStep(t('Credentials'), 'sourceDataForm');
$this->addStep(t('Select content to import'), 'contentSelectForm');
$this->addStep(t('Review'), 'reviewForm');

This reflects the typical workflow - the first page is obtains the raw data (by accepting database credentials, uploading a file, etc.), one or more pages present options to the user to decide what to import and how to map it to Drupal objects, and a final review form presents what will be done before the user hits the Finish button to register the migrations generated.

Next, you must define the getSourceName() method:

public function getSourceName() {
t('Example CMS');

This is used to present your wizard in the menu system - in this case, a subtab of the Migrate dashboard will be added with the label "Import from Example CMS".

Now, we get to the meat - defining the forms and validation callbacks for the wizard. These work much like normal form functions in Drupal, except that you don't need to add any submit buttons - the parent class automatically manages the Next, Previous, and Submit buttons. You only need to define the form elements that gather information from the user.

For each form referenced in an addStep call, you need to define two functions - the form function itself (e.g. 'sourceDataForm') and a validation function ('sourceDataFormValidate'). The form function simply needs to build and return a form array in the usual way. The validation function will do form validation in the usual way (e.g., calling form_set_error() if any input is invalid), but will also be expected to save the selections made by the user into member variables, where they will be accessible to later steps - especially the Review step, which will use that information to build argument arrays configuring the migrations that are to be registered.

A very powerful feature available to the validation steps is the addStep() call. You may recall we used it in the constructor to define some hard-coded wizard steps - but you can also call it in a validation function to add more steps. For example, in our case the only step we had between the source data form and the review form was contentSelectForm. It may be that categories are an optional feature in the source database - we didn't add a step for categories initially because we might not need it. We can add it when necessary:

protected function sourceDataFormValidate(&$form_state) {
// Use provided credentials to open the source database
// Look for category data in the source database
// Did we find any?
if ($categories_found) {
$this->addStep(t('Categories'), 'categoryForm', $this->currentStep);

Note we've used the optional third parameter to addStep here - the existing step that the new step will follow. If this is omitted, the new step is simply added to the end.

Now, one thing to keep in mind is that the wizard will ultimately register a migration group to hold the migrations you're defining. Thus, you can import from more than one source, and each will get a distinct group. Somewhere along the way - typically after the initial step, when you've accessed the source data - you need to set $this->groupName and $this->groupTitle, which will become the migration group's machine name and title. Ideally the source data will have some sort of unique name you can use to populate these, such as a site name or domain name.

Finally, the form builder for the last step (your review form) needs to pull all the information the wizard has gathered into suitable form for migration registration. For each migration you want to define, call addMigration() with the machine name, class name, and arguments you would normally pass to registerMigration():

->addMigration('User', 'ExampleUserMigration', array('md5_passwords' => TRUE))

Note that we pass the machine name as simply 'User'. The actual machine name that will be generated will have $this->groupName prepended to this, so that you end up with machine names that are unique among all migrations on the site.

Each of the migrations you pass to addMigration are saved away, and when the user clicks the Finish button the MigrateUIWizard class will register them and redirect to the Migrate dashboard. Note that the last step in your wizard should NOT have a validate function - all processing will be done by the MigrateUIWizard class.


The following modules have implemented wizards using this API - their code provides real-world examples of implementing a MigrateUIWizard class.

  1. Wordpress Migrate (relatively simple) - [Module Documentation]
  2. Drupal-to-Drupal data migration (more complex) - [Module Documentation]

Looking for support? Visit the forums, or join #drupal-support in IRC.