예제 #1
0
 /**
  * Tests rolling back configuration and content entities.
  */
 public function testRollback()
 {
     // We use vocabularies to demonstrate importing and rolling back
     // configuration entities.
     $vocabulary_data_rows = [['id' => '1', 'name' => 'categories', 'weight' => '2'], ['id' => '2', 'name' => 'tags', 'weight' => '1']];
     $ids = ['id' => ['type' => 'integer']];
     $config = ['id' => 'vocabularies', 'migration_tags' => ['Import and rollback test'], 'source' => ['plugin' => 'embedded_data', 'data_rows' => $vocabulary_data_rows, 'ids' => $ids], 'process' => ['vid' => 'id', 'name' => 'name', 'weight' => 'weight'], 'destination' => ['plugin' => 'entity:taxonomy_vocabulary']];
     $vocabulary_migration = Migration::create($config);
     $vocabulary_id_map = $vocabulary_migration->getIdMap();
     $this->assertTrue($vocabulary_migration->getDestinationPlugin()->supportsRollback());
     // Import and validate vocabulary config entities were created.
     $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this);
     $vocabulary_executable->import();
     foreach ($vocabulary_data_rows as $row) {
         /** @var Vocabulary $vocabulary */
         $vocabulary = Vocabulary::load($row['id']);
         $this->assertTrue($vocabulary);
         $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertNotNull($map_row['destid1']);
     }
     // We use taxonomy terms to demonstrate importing and rolling back
     // content entities.
     $term_data_rows = [['id' => '1', 'vocab' => '1', 'name' => 'music'], ['id' => '2', 'vocab' => '2', 'name' => 'Bach'], ['id' => '3', 'vocab' => '2', 'name' => 'Beethoven']];
     $ids = ['id' => ['type' => 'integer']];
     $config = ['id' => 'terms', 'migration_tags' => ['Import and rollback test'], 'source' => ['plugin' => 'embedded_data', 'data_rows' => $term_data_rows, 'ids' => $ids], 'process' => ['tid' => 'id', 'vid' => 'vocab', 'name' => 'name'], 'destination' => ['plugin' => 'entity:taxonomy_term'], 'migration_dependencies' => ['required' => ['vocabularies']]];
     $term_migration = Migration::create($config);
     $term_id_map = $term_migration->getIdMap();
     $this->assertTrue($term_migration->getDestinationPlugin()->supportsRollback());
     // Import and validate term entities were created.
     $term_executable = new MigrateExecutable($term_migration, $this);
     $term_executable->import();
     foreach ($term_data_rows as $row) {
         /** @var Term $term */
         $term = Term::load($row['id']);
         $this->assertTrue($term);
         $map_row = $term_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertNotNull($map_row['destid1']);
     }
     // Rollback and verify the entities are gone.
     $term_executable->rollback();
     foreach ($term_data_rows as $row) {
         $term = Term::load($row['id']);
         $this->assertNull($term);
         $map_row = $term_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertFalse($map_row);
     }
     $vocabulary_executable->rollback();
     foreach ($vocabulary_data_rows as $row) {
         $term = Vocabulary::load($row['id']);
         $this->assertNull($term);
         $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertFalse($map_row);
     }
     // Test that simple configuration is not rollbackable.
     $term_setting_rows = [['id' => 1, 'override_selector' => '0', 'terms_per_page_admin' => '10']];
     $ids = ['id' => ['type' => 'integer']];
     $config = ['id' => 'taxonomy_settings', 'migration_tags' => ['Import and rollback test'], 'source' => ['plugin' => 'embedded_data', 'data_rows' => $term_setting_rows, 'ids' => $ids], 'process' => ['override_selector' => 'override_selector', 'terms_per_page_admin' => 'terms_per_page_admin'], 'destination' => ['plugin' => 'config', 'config_name' => 'taxonomy.settings'], 'migration_dependencies' => ['required' => ['vocabularies']]];
     $settings_migration = Migration::create($config);
     $this->assertFalse($settings_migration->getDestinationPlugin()->supportsRollback());
 }
 /**
  * Test importing and rolling back our data.
  */
 public function testMigrations()
 {
     /** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
     $storage = $this->container->get('entity.manager')->getStorage('node');
     $this->assertEquals(0, count($storage->loadMultiple()));
     // Run the migrations.
     $migration_ids = ['external_translated_test_node', 'external_translated_test_node_translation'];
     $this->executeMigrations($migration_ids);
     $this->assertEquals(3, count($storage->loadMultiple()));
     $node = $storage->load(1);
     $this->assertEquals('en', $node->language()->getId());
     $this->assertEquals('Cat', $node->title->value);
     $this->assertEquals('Chat', $node->getTranslation('fr')->title->value);
     $this->assertEquals('Gato', $node->getTranslation('es')->title->value);
     $node = $storage->load(2);
     $this->assertEquals('en', $node->language()->getId());
     $this->assertEquals('Dog', $node->title->value);
     $this->assertEquals('Chien', $node->getTranslation('fr')->title->value);
     $this->assertFalse($node->hasTranslation('es'), "No spanish translation for node 2");
     $node = $storage->load(3);
     $this->assertEquals('en', $node->language()->getId());
     $this->assertEquals('Monkey', $node->title->value);
     $this->assertFalse($node->hasTranslation('fr'), "No french translation for node 3");
     $this->assertFalse($node->hasTranslation('es'), "No spanish translation for node 3");
     $this->assertNull($storage->load(4), "No node 4 migrated");
     // Roll back the migrations.
     foreach ($migration_ids as $migration_id) {
         $migration = $this->getMigration($migration_id);
         $executable = new MigrateExecutable($migration, $this);
         $executable->rollback();
     }
     $this->assertEquals(0, count($storage->loadMultiple()));
 }
예제 #3
0
 /**
  * Runs a single migration batch.
  *
  * @param int[] $initial_ids
  *   The full set of migration IDs to import.
  * @param string $operation
  *   The operation to perform, 'import' or 'rollback'.
  * @param array $context
  *   The batch context.
  */
 public static function run($initial_ids, $operation, &$context)
 {
     if (!static::$listenersAdded) {
         $event_dispatcher = \Drupal::service('event_dispatcher');
         if ($operation == 'import') {
             $event_dispatcher->addListener(MigrateEvents::POST_ROW_SAVE, [static::class, 'onPostRowSave']);
             $event_dispatcher->addListener(MigrateEvents::MAP_SAVE, [static::class, 'onMapSave']);
             $event_dispatcher->addListener(MigrateEvents::IDMAP_MESSAGE, [static::class, 'onIdMapMessage']);
         } else {
             $event_dispatcher->addListener(MigrateEvents::POST_ROW_DELETE, [static::class, 'onPostRowDelete']);
             $event_dispatcher->addListener(MigrateEvents::MAP_DELETE, [static::class, 'onMapDelete']);
         }
         static::$maxExecTime = ini_get('max_execution_time');
         if (static::$maxExecTime <= 0) {
             static::$maxExecTime = 60;
         }
         // Set an arbitrary threshold of 3 seconds (e.g., if max_execution_time is
         // 45 seconds, we will quit at 42 seconds so a slow item or cleanup
         // overhead don't put us over 45).
         static::$maxExecTime -= 3;
         static::$listenersAdded = TRUE;
     }
     if (!isset($context['sandbox']['migration_ids'])) {
         $context['sandbox']['max'] = count($initial_ids);
         $context['sandbox']['current'] = 1;
         // Total number processed for this migration.
         $context['sandbox']['num_processed'] = 0;
         // migration_ids will be the list of IDs remaining to run.
         $context['sandbox']['migration_ids'] = $initial_ids;
         $context['sandbox']['messages'] = [];
         $context['results']['failures'] = 0;
         $context['results']['successes'] = 0;
         $context['results']['operation'] = $operation;
     }
     // Number processed in this batch.
     static::$numProcessed = 0;
     $migration_id = reset($context['sandbox']['migration_ids']);
     /** @var \Drupal\migrate\Entity\Migration $migration */
     $migration = Migration::load($migration_id);
     if ($migration) {
         static::$messages = new MigrateMessageCapture();
         $executable = new MigrateExecutable($migration, static::$messages);
         $migration_name = $migration->label() ? $migration->label() : $migration_id;
         try {
             if ($operation == 'import') {
                 $migration_status = $executable->import();
             } else {
                 $migration_status = $executable->rollback();
             }
         } catch (\Exception $e) {
             static::logger()->error($e->getMessage());
             $migration_status = MigrationInterface::RESULT_FAILED;
         }
         switch ($migration_status) {
             case MigrationInterface::RESULT_COMPLETED:
                 // Store the number processed in the sandbox.
                 $context['sandbox']['num_processed'] += static::$numProcessed;
                 if ($operation == 'import') {
                     $message = static::getTranslation()->formatPlural($context['sandbox']['num_processed'], 'Upgraded @migration (processed 1 item total)', 'Upgraded @migration (processed @num_processed items total)', ['@migration' => $migration_name, '@num_processed' => $context['sandbox']['num_processed']]);
                 } else {
                     $message = static::getTranslation()->formatPlural($context['sandbox']['num_processed'], 'Rolled back @migration (processed 1 item total)', 'Rolled back @migration (processed @num_processed items total)', ['@migration' => $migration_name, '@num_processed' => $context['sandbox']['num_processed']]);
                     $migration->delete();
                 }
                 $context['sandbox']['messages'][] = $message;
                 static::logger()->notice($message);
                 $context['sandbox']['num_processed'] = 0;
                 $context['results']['successes']++;
                 break;
             case MigrationInterface::RESULT_INCOMPLETE:
                 $context['sandbox']['messages'][] = static::getTranslation()->formatPlural(static::$numProcessed, 'Continuing with @migration (processed 1 item)', 'Continuing with @migration (processed @num_processed items)', ['@migration' => $migration_name, '@num_processed' => static::$numProcessed]);
                 $context['sandbox']['num_processed'] += static::$numProcessed;
                 break;
             case MigrationInterface::RESULT_STOPPED:
                 $context['sandbox']['messages'][] = t('Operation stopped by request');
                 break;
             case MigrationInterface::RESULT_FAILED:
                 $context['sandbox']['messages'][] = t('Operation on @migration failed', ['@migration' => $migration_name]);
                 $context['results']['failures']++;
                 static::logger()->error('Operation on @migration failed', ['@migration' => $migration_name]);
                 break;
             case MigrationInterface::RESULT_SKIPPED:
                 $context['sandbox']['messages'][] = t('Operation on @migration skipped due to unfulfilled dependencies', ['@migration' => $migration_name]);
                 static::logger()->error('Operation on @migration skipped due to unfulfilled dependencies', ['@migration' => $migration_name]);
                 break;
             case MigrationInterface::RESULT_DISABLED:
                 // Skip silently if disabled.
                 break;
         }
         // Unless we're continuing on with this migration, take it off the list.
         if ($migration_status != MigrationInterface::RESULT_INCOMPLETE) {
             array_shift($context['sandbox']['migration_ids']);
             $context['sandbox']['current']++;
         }
         // Add and log any captured messages.
         foreach (static::$messages->getMessages() as $message) {
             $context['sandbox']['messages'][] = $message;
             static::logger()->error($message);
         }
         // Only display the last MESSAGE_LENGTH messages, in reverse order.
         $message_count = count($context['sandbox']['messages']);
         $context['message'] = '';
         for ($index = max(0, $message_count - self::MESSAGE_LENGTH); $index < $message_count; $index++) {
             $context['message'] = $context['sandbox']['messages'][$index] . "<br />\n" . $context['message'];
         }
         if ($message_count > self::MESSAGE_LENGTH) {
             // Indicate there are earlier messages not displayed.
             $context['message'] .= '&hellip;';
         }
         // At the top of the list, display the next one (which will be the one
         // that is running while this message is visible).
         if (!empty($context['sandbox']['migration_ids'])) {
             $migration_id = reset($context['sandbox']['migration_ids']);
             $migration = Migration::load($migration_id);
             $migration_name = $migration->label() ? $migration->label() : $migration_id;
             if ($operation == 'import') {
                 $context['message'] = t('Currently upgrading @migration (@current of @max total tasks)', ['@migration' => $migration_name, '@current' => $context['sandbox']['current'], '@max' => $context['sandbox']['max']]) . "<br />\n" . $context['message'];
             } else {
                 $context['message'] = t('Currently rolling back @migration (@current of @max total tasks)', ['@migration' => $migration_name, '@current' => $context['sandbox']['current'], '@max' => $context['sandbox']['max']]) . "<br />\n" . $context['message'];
             }
         }
     } else {
         array_shift($context['sandbox']['migration_ids']);
         $context['sandbox']['current']++;
     }
     $context['finished'] = 1 - count($context['sandbox']['migration_ids']) / $context['sandbox']['max'];
 }
 /**
  * Tests rolling back configuration and content entities.
  */
 public function testRollback()
 {
     // We use vocabularies to demonstrate importing and rolling back
     // configuration entities.
     $vocabulary_data_rows = [['id' => '1', 'name' => 'categories', 'weight' => '2'], ['id' => '2', 'name' => 'tags', 'weight' => '1']];
     $ids = ['id' => ['type' => 'integer']];
     $definition = ['id' => 'vocabularies', 'migration_tags' => ['Import and rollback test'], 'source' => ['plugin' => 'embedded_data', 'data_rows' => $vocabulary_data_rows, 'ids' => $ids], 'process' => ['vid' => 'id', 'name' => 'name', 'weight' => 'weight'], 'destination' => ['plugin' => 'entity:taxonomy_vocabulary']];
     $vocabulary_migration = new Migration([], uniqid(), $definition);
     $vocabulary_id_map = $vocabulary_migration->getIdMap();
     $this->assertTrue($vocabulary_migration->getDestinationPlugin()->supportsRollback());
     // Import and validate vocabulary config entities were created.
     $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this);
     $vocabulary_executable->import();
     foreach ($vocabulary_data_rows as $row) {
         /** @var Vocabulary $vocabulary */
         $vocabulary = Vocabulary::load($row['id']);
         $this->assertTrue($vocabulary);
         $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertNotNull($map_row['destid1']);
     }
     // We use taxonomy terms to demonstrate importing and rolling back content
     // entities.
     $term_data_rows = [['id' => '1', 'vocab' => '1', 'name' => 'music'], ['id' => '2', 'vocab' => '2', 'name' => 'Bach'], ['id' => '3', 'vocab' => '2', 'name' => 'Beethoven']];
     $ids = ['id' => ['type' => 'integer']];
     $definition = ['id' => 'terms', 'migration_tags' => ['Import and rollback test'], 'source' => ['plugin' => 'embedded_data', 'data_rows' => $term_data_rows, 'ids' => $ids], 'process' => ['tid' => 'id', 'vid' => 'vocab', 'name' => 'name'], 'destination' => ['plugin' => 'entity:taxonomy_term'], 'migration_dependencies' => ['required' => ['vocabularies']]];
     $term_migration = new Migration([], uniqid(), $definition);
     $term_id_map = $term_migration->getIdMap();
     $this->assertTrue($term_migration->getDestinationPlugin()->supportsRollback());
     // Pre-create a term, to make sure it isn't deleted on rollback.
     $preserved_term_ids[] = 1;
     $new_term = Term::create(['tid' => 1, 'vid' => 1, 'name' => 'music']);
     $new_term->save();
     // Import and validate term entities were created.
     $term_executable = new MigrateExecutable($term_migration, $this);
     $term_executable->import();
     // Also explicitly mark one row to be preserved on rollback.
     $preserved_term_ids[] = 2;
     $map_row = $term_id_map->getRowBySource(['id' => 2]);
     $dummy_row = new Row(['id' => 2], $ids);
     $term_id_map->saveIdMapping($dummy_row, [$map_row['destid1']], $map_row['source_row_status'], MigrateIdMapInterface::ROLLBACK_PRESERVE);
     foreach ($term_data_rows as $row) {
         /** @var Term $term */
         $term = Term::load($row['id']);
         $this->assertTrue($term);
         $map_row = $term_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertNotNull($map_row['destid1']);
     }
     // Rollback and verify the entities are gone.
     $term_executable->rollback();
     foreach ($term_data_rows as $row) {
         $term = Term::load($row['id']);
         if (in_array($row['id'], $preserved_term_ids)) {
             $this->assertNotNull($term);
         } else {
             $this->assertNull($term);
         }
         $map_row = $term_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertFalse($map_row);
     }
     $vocabulary_executable->rollback();
     foreach ($vocabulary_data_rows as $row) {
         $term = Vocabulary::load($row['id']);
         $this->assertNull($term);
         $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
         $this->assertFalse($map_row);
     }
     // Test that simple configuration is not rollbackable.
     $term_setting_rows = [['id' => 1, 'override_selector' => '0', 'terms_per_page_admin' => '10']];
     $ids = ['id' => ['type' => 'integer']];
     $definition = ['id' => 'taxonomy_settings', 'migration_tags' => ['Import and rollback test'], 'source' => ['plugin' => 'embedded_data', 'data_rows' => $term_setting_rows, 'ids' => $ids], 'process' => ['override_selector' => 'override_selector', 'terms_per_page_admin' => 'terms_per_page_admin'], 'destination' => ['plugin' => 'config', 'config_name' => 'taxonomy.settings'], 'migration_dependencies' => ['required' => ['vocabularies']]];
     $settings_migration = new Migration([], uniqid(), $definition);
     $this->assertFalse($settings_migration->getDestinationPlugin()->supportsRollback());
 }