Issue #719650 by mikeryan: Implemented ability to assign migrations to groups, and...
authorMike Ryan
Sun, 10 Jul 2011 18:40:32 +0000 (14:40 -0400)
committerMike Ryan
Sun, 10 Jul 2011 18:40:32 +0000 (14:40 -0400)
CHANGELOG.txt
includes/base.inc
includes/group.inc [new file with mode: 0644]
includes/migration.inc
migrate.drush.inc
migrate.info
migrate.module
migrate_example/wine.inc

index 8cdc160..79bdc96 100644 (file)
@@ -3,6 +3,7 @@ Next release
 ============
 
 Features and enhancements
+- #719650 - Implemented ability to assign migrations to groups, and run by group.
 - #1201762 - Implemented built-in caching for source counts, and flag to enable it.
 - #1205278 - Added preserve_files argument to MigrateFileFieldHandler.
 - #1205278 - Added preserve_files option to MigrateDestinationFile.
index fedac75..51b6a70 100644 (file)
@@ -37,6 +37,16 @@ abstract class MigrationBase {
   }
 
   /**
+   * The name of a migration group, used to collect related migrations.
+   *
+   * @var string
+   */
+  protected $group;
+  public function getGroup() {
+    return $this->group;
+  }
+
+  /**
    * Detailed information describing the migration.
    *
    * @var string
@@ -276,9 +286,16 @@ abstract class MigrationBase {
   /**
    * General initialization of a MigrationBase object.
    */
-  public function __construct() {
+  public function __construct($group = NULL) {
     $this->machineName = $this->generateMachineName();
 
+    if (empty($group)) {
+      $this->group = MigrateGroup::getInstance('default');
+    }
+    else {
+      $this->group = $group;
+    }
+
     // Record the memory limit in bytes
     $limit = trim(ini_get('memory_limit'));
     if ($limit == '-1') {
diff --git a/includes/group.inc b/includes/group.inc
new file mode 100644 (file)
index 0000000..920419b
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Definition for a migration group.
+ */
+
+class MigrateGroup {
+  /**
+   * The name of the group - used to identify it in drush commands.
+   *
+   * @var string
+   */
+  protected $name;
+  public function getName() {
+    return $this->name;
+  }
+
+  /**
+   * List of groups this group is dependent on.
+   *
+   * @var array
+   */
+  protected $dependencies = array();
+  public function getDependencies() {
+    return $this->dependencies;
+  }
+
+  /**
+   * The central list of all known groups, keyed by group name.
+   *
+   * @var array
+   */
+  static protected $groupList = array();
+  static public function groups() {
+    $groups = array();
+    $dependent_groups = array();
+    $required_groups = array();
+    foreach (self::$groupList as $name => $group) {
+      $dependencies = $group->getDependencies();
+      if (count($dependencies) > 0) {
+        // Set groups with dependencies aside for reordering
+        $dependent_groups[$name] = $group;
+        $required_groups += $dependencies;
+      }
+      else {
+        // No dependencies, just add
+        $groups[$name] = $group;
+      }
+    }
+    $iterations = 0;
+    while (count($dependent_groups) > 0) {
+      if ($iterations++ > 20) {
+        $group_names = implode(',', array_keys($dependent_groups));
+        throw new MigrateException(t('Failure to sort migration list - most likely due ' .
+              'to circular dependencies involving groups !group_names',
+        array('!group_names' => $group_names)));
+      }
+      foreach ($dependent_groups as $name => $group) {
+        $ready = TRUE;
+        // Scan all the dependencies for this group and make sure they're all
+        // in the final list
+        foreach ($group->getDependencies() as $dependency) {
+          if (!isset($groups[$dependency])) {
+            $ready = FALSE;
+            break;
+          }
+        }
+        if ($ready) {
+          // Yes they are! Move this group to the final list
+          $groups[$name] = $group;
+          unset($dependent_groups[$name]);
+        }
+      }
+    }
+
+    return $groups;
+  }
+
+  /**
+   * Basic constructor.
+   *
+   * @param string $name
+   *  Group name.
+   *
+   * @param array $dependencies
+   *  List of dependent groups.
+   */
+  public function __construct($name, $dependencies = array()) {
+    $this->name = $name;
+    $this->dependencies = $dependencies;
+  }
+
+  /**
+   * Retrieve (creating if necessary) an instance of the named group.
+   *
+   * @param string $name
+   *  Group name.
+   *
+   * @param array $dependencies
+   *  List of dependent groups.
+   */
+  static public function getInstance($name, $dependencies = array()) {
+    if (empty(self::$groupList[$name])) {
+      self::$groupList[$name] = new MigrateGroup($name, $dependencies);
+    }
+    return self::$groupList[$name];
+  }
+}
index 8cc10f3..cb67077 100644 (file)
@@ -122,8 +122,8 @@ abstract class Migration extends MigrationBase {
   /**
    * General initialization of a Migration object.
    */
-  public function __construct() {
-    parent::__construct();
+  public function __construct($group = NULL) {
+    parent::__construct($group);
   }
 
   /**
index 41e1385..e0a3446 100644 (file)
@@ -582,6 +582,13 @@ function drush_migrate_get_migrations($args) {
       }
     }
   }
+  else if ($group = drush_get_option('group')) {
+    foreach ($migration_objects as $name => $migration) {
+      if (strtolower($group) != strtolower($migration->getGroup()->getName()) || !$migration->getEnabled()) {
+        unset($migration_objects[$name]);
+      }
+    }
+  }
   else {
     $named_migrations = array();
     foreach (explode(',', $args) as $name) {
@@ -629,13 +636,18 @@ function drush_migrate_rollback_validate($args = NULL) {
 
 function drush_migrate_validate_common($args) {
   if (drush_get_option('all')) {
-    if (!empty($args)) {
-      return drush_set_error(NULL, dt('You must specify either a migration name or --all, not both'));
+    if (!empty($args) || drush_get_option('group')) {
+      return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
+    }
+  }
+  else if (drush_get_option('group')) {
+    if (!empty($args) || drush_get_option('all')) {
+      return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
     }
   }
   else {
     if (empty($args)) {
-      return drush_set_error(NULL, dt('You must specify either a migration name or the --all option'));
+      return drush_set_error(NULL, dt('You must specify exactly one of a migration name, --all, or --group'));
     }
     $machine_names = explode(',', $args);
 
index d44f451..49337e1 100755 (executable)
@@ -8,6 +8,7 @@ files[] = includes/field_mapping.inc
 files[] = includes/migration.inc
 files[] = includes/destination.inc
 files[] = includes/exception.inc
+files[] = includes/group.inc
 files[] = includes/handler.inc
 files[] = includes/map.inc
 files[] = includes/source.inc
index c86602d..8c182ed 100755 (executable)
@@ -83,6 +83,28 @@ function migrate_migrations() {
       }
     }
   }
+
+  // The migrations are now ordered according to their own dependencies - now order
+  // them by group
+  $groups = MigrateGroup::groups();
+  // Seed the final list by properly-ordered groups.
+  $final_migrations = array();
+  foreach ($groups as $name => $group) {
+    $final_migrations[$name] = array();
+  }
+
+  // Fill in the grouped list
+  foreach ($migrations as $machine_name => $migration) {
+    $final_migrations[$migration->getGroup()->getName()][$machine_name] = $migration;
+  }
+  // Then flatten the list
+  $migrations = array();
+  foreach ($final_migrations as $group_name => $group_migrations) {
+    foreach ($group_migrations as $machine_name => $migration) {
+      $migrations[$machine_name] = $migration;
+    }
+  }
+
   return $migrations;
 }
 
index a1b19c2..552df5d 100644 (file)
@@ -17,7 +17,17 @@ abstract class AdvancedExampleMigration extends Migration {
   public $basicFormat;
 
   public function __construct() {
-    parent::__construct();
+    // TIP: Migrations can be organized into groups. In this case, all the migrations
+    // derived from AdvancedExampleMigration will be part of the 'wine' group.
+    // This enables us to easily run just the wine example migrations:
+    //  drush migrate-import --group=wine
+    // The second argument to MigrateGroup::getInstance is an array of groups
+    // which should come before this when viewing migration statuses, or running
+    // migration operations using --all. Since the beer migrations in this module
+    // did not specify a group, it is in the 'default' group, so this constructor
+    // indicates that the wine migrations come after the beer migrations.
+    parent::__construct(MigrateGroup::getInstance('wine', array('default')));
+
     $this->team = array(
       new MigrateTeamMember('Jack Kramer', 'jkramer@example.com', t('Taster')),
       new MigrateTeamMember('Linda Madison', 'lmadison@example.com', t('Winemaker')),
@@ -41,16 +51,10 @@ class WinePrepMigration extends MigrationBase {
   public static $wasEnabled = FALSE;
 
   public function __construct() {
-    parent::__construct();
+    // Because we're derived directly from migrationBase rather than AdvancedExampleMigration,
+    // we must specify the group again here.
+    parent::__construct(MigrateGroup::getInstance('wine', array('default')));
     $this->description = t('If auto_nodetitle is present, disable it for the duration');
-    // TIP: Regular dependencies, besides enforcing (in the absence of --force)
-    // the run order of migrations, affect the sorting of migrations on display.
-    // You can use soft dependencies to affect just the display order when the
-    // migrations aren't technically required to run in a certain order. In this
-    // case, we want the wine migrations to appear after the beer migrations -
-    // without this line, they would be intermingled due to their lack of
-    // (formal) interdependencies.
-    $this->softDependencies = array('BeerComment');
   }
   // Define isComplete(), returning a boolean, to indicate whether dependent
   // migrations may proceed
@@ -224,8 +228,14 @@ class WineFileBlobMigration extends AdvancedExampleMigration {
 
 class WineRoleMigration extends XMLMigration {
   public function __construct() {
-    parent::__construct();
+    parent::__construct(MigrateGroup::getInstance('wine', array('default')));
     $this->description = t('XML feed (multi items) of roles (positions)');
+
+    // TIP: Regular dependencies, besides enforcing (in the absence of --force)
+    // the run order of migrations, affect the sorting of migrations on display.
+    // You can use soft dependencies to affect just the display order when the
+    // migrations aren't technically required to run in a certain order. In this
+    // case, we want the role migration to appear after the file migration.
     $this->softDependencies = array('WineFileCopy');
 
     // There isn't a consistent way to automatically identify appropriate "fields"
@@ -404,7 +414,7 @@ class WineProducerMigration extends AdvancedExampleMigration {
  */
 class WineProducerXMLMigration extends XMLMigration {
   public function __construct() {
-    parent::__construct();
+    parent::__construct(MigrateGroup::getInstance('wine', array('default')));
     $this->description = t('XML feed of wine producers of the world');
     $this->dependencies = array('WineRegion', 'WineUser');
 
@@ -475,7 +485,7 @@ class WineProducerXMLMigration extends XMLMigration {
  */
 class WineProducerMultiXMLMigration extends XMLMigration {
   public function __construct() {
-    parent::__construct();
+    parent::__construct(MigrateGroup::getInstance('wine', array('default')));
     $this->description = t('XML feed (multi items) of wine producers of the world');
     $this->dependencies = array('WineRegion', 'WineUser');
 
@@ -782,7 +792,7 @@ class WineTableMigration extends AdvancedExampleMigration {
 
 class WineFinishMigration extends MigrationBase {
   public function __construct() {
-    parent::__construct();
+    parent::__construct(MigrateGroup::getInstance('wine', array('default')));
     $this->description = t('If auto_nodetitle is present and was previously enabled,
       re-enable it');
     $this->dependencies = array('WineComment');