diff --git a/core/modules/migrate/config/schema/migrate.schema.yml b/core/modules/migrate/config/schema/migrate.schema.yml index c48bdd1..d1c4bff 100644 --- a/core/modules/migrate/config/schema/migrate.schema.yml +++ b/core/modules/migrate/config/schema/migrate.schema.yml @@ -13,6 +13,9 @@ migrate.migration.*: sequence: type: string label: 'Group' + migration_group: + type: string + label: 'Active group' label: type: label label: 'Label' @@ -44,3 +47,20 @@ migrate.migration.*: sequence: type: string label: 'Dependency' + +migrate.migration_group.*: + type: config_entity + label: 'Migration Group' + mapping: + id: + type: string + label: 'ID' + label: + type: label + label: 'Label' + module: + type: string + label: 'Dependent module' + shared_configuration: + type: ignore + label: 'Shared migration configuration' diff --git a/core/modules/migrate/src/Entity/MigrationGroup.php b/core/modules/migrate/src/Entity/MigrationGroup.php new file mode 100644 index 0000000..9fd8bf9 --- /dev/null +++ b/core/modules/migrate/src/Entity/MigrationGroup.php @@ -0,0 +1,82 @@ +condition('migration_group', $this->id()); + $names = $query->execute(); + + // Order the migrations according to their dependencies. + /** @var MigrationInterface[] $migrations */ + $migrations = \Drupal::entityManager()->getStorage('migration')->loadMultiple($names); + + // Delete in reverse order, so dependencies are never violated. + $migrations = array_reverse($migrations); + + foreach ($migrations as $migration) { + $migration->delete(); + } + + // Finally, delete the group itself. + parent::delete(); + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + parent::calculateDependencies(); + // Make sure we save any explicit module dependencies. + if ($provider = $this->get('module')) { + $this->addDependency('module', $provider); + } + return $this->dependencies; + } + +} diff --git a/core/modules/migrate/src/Entity/MigrationGroupInterface.php b/core/modules/migrate/src/Entity/MigrationGroupInterface.php new file mode 100644 index 0000000..7461477 --- /dev/null +++ b/core/modules/migrate/src/Entity/MigrationGroupInterface.php @@ -0,0 +1,17 @@ +configFactory = $config_factory; + $this->uuidService = $uuid_service; + $this->languageManager = $language_manager; + $this->migrationGroupStorage = $migration_group_storage; + } + + /** + * {@inheritdoc} + */ + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, + $container->get('config.factory'), + $container->get('uuid'), + $container->get('language_manager'), + $container->get('entity_manager')->getStorage('migration_group') + ); + } + + /** * {@inheritdoc} */ public function buildDependencyMigration(array $migrations, array $dynamic_ids) { @@ -90,4 +141,42 @@ protected function addDependency(array &$graph, $id, $dependency, $dynamic_ids) $graph[$id]['edges'] += array_combine($dependencies, $dependencies); } + /** + * {@inheritdoc} + */ + protected function doLoadMultiple(array $ids = NULL) { + $migrations = parent::doLoadMultiple($ids); + + /** @var MigrationInterface $migration */ + foreach ($migrations as $migration) { + // If we are pointing to a valid group, merge its properties into ours. + if (isset($migration->migration_group)) { + /** @var MigrationGroupInterface $group */ + $group = $this->migrationGroupStorage->load($migration->migration_group); + if ($group) { + $shared_configuration = $group->get('shared_configuration'); + if ($shared_configuration) { + foreach ($shared_configuration as $key => $group_value) { + $migration_value = $migration->get($key); + // Where both the migration and the group provide arrays, + // replace recursively (so each key collision is resolved in favor + // of the migration). + if (is_array($migration_value) && is_array($group_value)) { + $merged_values = array_replace_recursive($group_value, $migration_value); + $migration->set($key, $merged_values); + } + // Where the group provides a value the migration doesn't, use + // the group value. + elseif (is_null($migration_value)) { + $migration->set($key, $group_value); + } + // Otherwise, the existing migration value overrides the group value. + } + } + } + } + } + return $migrations; + } + } diff --git a/core/modules/migrate/src/Tests/MigrationGroupTest.php b/core/modules/migrate/src/Tests/MigrationGroupTest.php new file mode 100644 index 0000000..95b6e49 --- /dev/null +++ b/core/modules/migrate/src/Tests/MigrationGroupTest.php @@ -0,0 +1,113 @@ +strictConfigSchema = FALSE; + parent::setUp(); + } + + /** + * Test that group configuration is properly merged into specific migrations. + */ + public function testConfigurationMerge() { + $group_id = 'test_group'; + + /** @var MigrationGroupInterface $migration_group */ + $migration_group = entity_create('migration_group', array()); + $migration_group->set('id', $group_id); + $migration_group->set('shared_configuration', array( + 'migration_groups' => array('Drupal 6'), // In migration, so will be overrideen. + 'source' => array( + 'constants' => array( + 'type' => 'image', // Not in migration, so will be added. + 'cardinality' => '1', // In migration, so will be overridden. + ), + ), + 'destination' => array('entity' => 'field_storage_config'), // Not in migration, so will be added. + )); + $migration_group->save(); + + /** @var MigrationInterface $migration */ + $migration = entity_create('migration', array()); + $migration->set('id', 'specific_migration'); + $migration->set('migration_group', $group_id); + $migration->set('label', 'Unaffected by the group'); + $migration->set('migration_groups', array('Drupal 7')); // Overrides group. + $migration->set('source', array( + 'plugin' => 'empty', // Not in group, persists. + 'constants' => array( + 'entity_type' => 'user', // Not in group, persists. + 'cardinality' => '3', // Overrides group. + ), + )); + $migration->save(); + + $expected_config = array( + 'migration_group' => $group_id, + 'label' => 'Unaffected by the group', + 'migration_groups' => array('Drupal 7'), + 'source' => array( + 'plugin' => 'empty', + 'constants' => array( + 'entity_type' => 'user', + 'type' => 'image', + 'cardinality' => '3', + ), + ), + 'destination' => array('entity' => 'field_storage_config'), + ); + /** @var MigrationInterface $loaded_migration */ + $loaded_migration = entity_load('migration', 'specific_migration', TRUE); + foreach ($expected_config as $key => $expected_value) { + $actual_value = $loaded_migration->get($key); + $this->assertEqual($expected_value, $actual_value); + } + } + + /** + * Test that deleting a group deletes its migrations. + */ + public function testDelete() { + /** @var MigrationGroupInterface $migration_group */ + $migration_group = entity_create('migration_group', array()); + $migration_group->set('id', 'test_group'); + $migration_group->save(); + + /** @var MigrationInterface $migration */ + $migration = entity_create('migration', array()); + $migration->set('id', 'specific_migration'); + $migration->set('migration_group', 'test_group'); + $migration->save(); + + /** @var MigrationGroupInterface $loaded_migration_group */ + $loaded_migration_group = entity_load('migration_group', 'test_group', TRUE); + $loaded_migration_group->delete(); + + /** @var MigrationGroupInterface $loaded_migration */ + $loaded_migration = entity_load('migration', 'specific_migration', TRUE); + $this->assertNull($loaded_migration); + } + +}