Issue #719650 by mikeryan: Implemented ability to assign migrations to groups, and...
authorMike Ryan
Sun, 10 Jul 2011 18:57:30 +0000 (14:57 -0400)
committerMike Ryan
Sun, 10 Jul 2011 18:57:30 +0000 (14:57 -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 8d598e9..c462aa0 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.
 - #1195802 - Added prepareRow() support for MSSQL plugin.
 - #1195784 - Added verification of required extension to MSSQL plugin.
index 72daf56..44448aa 100644 (file)
@@ -45,6 +45,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
@@ -284,9 +294,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 3927d8f..72a136d 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) {
@@ -634,13 +641,18 @@ function drush_migrate_validate_common($args) {
   $user = $user_one;
   session_save_session(FALSE);
   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 7bf848e..350247b 100755 (executable)
@@ -12,6 +12,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 5bdac4c..111828b 100755 (executable)
@@ -85,6 +85,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 170ab1c..02fc7b4 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')),
@@ -47,17 +57,13 @@ 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
   public function isComplete() {
@@ -151,8 +157,14 @@ class WineBestWithMigration extends WineTermMigration {
 
 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('WineBestWith');
 
     // There isn't a consistent way to automatically identify appropriate "fields"
@@ -295,7 +307,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');
 
@@ -365,7 +377,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');
 
@@ -671,7 +683,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');