============
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.
}
/**
+ * 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
/**
* 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') {
--- /dev/null
+<?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];
+ }
+}
/**
* General initialization of a Migration object.
*/
- public function __construct() {
- parent::__construct();
+ public function __construct($group = NULL) {
+ parent::__construct($group);
}
/**
}
}
}
+ 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) {
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);
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
}
}
}
+
+ // 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;
}
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')),
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
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"
*/
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');
*/
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');
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');