/** * Runs a single migration batch. * * @param int[] $initial_ids * The full set of migration IDs to import. * @param string $operation * The operation to perform. Only 'import' is currently supported. * @param array $config * An array of additional configuration from the form. * @param array $context * The batch context. * * @todo Remove the $operation parameter and conditionals for it below, and * refactor this method. https://www.drupal.org/node/2687851 */ public static function run($initial_ids, $operation, $config, &$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']); } 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']); $definition = \Drupal::service('plugin.manager.migration')->getDefinition($migration_id); $configuration = []; // @todo Find a way to avoid this in https://www.drupal.org/node/2804611. if ($definition['destination']['plugin'] === 'entity:file') { // Make sure we have a single trailing slash. $configuration['source']['constants']['source_base_path'] = rtrim($config['source_base_path'], '/') . '/'; } /** @var \Drupal\migrate\Plugin\Migration $migration */ $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id, $configuration); 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(); } } 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 = new PluralTranslatableMarkup($context['sandbox']['num_processed'], 'Upgraded @migration (processed 1 item total)', 'Upgraded @migration (processed @count items total)', ['@migration' => $migration_name]); } $context['sandbox']['messages'][] = (string) $message; static::logger()->notice($message); $context['sandbox']['num_processed'] = 0; $context['results']['successes']++; break; case MigrationInterface::RESULT_INCOMPLETE: $context['sandbox']['messages'][] = (string) new PluralTranslatableMarkup(static::$numProcessed, 'Continuing with @migration (processed 1 item)', 'Continuing with @migration (processed @count items)', ['@migration' => $migration_name]); $context['sandbox']['num_processed'] += static::$numProcessed; break; case MigrationInterface::RESULT_STOPPED: $context['sandbox']['messages'][] = (string) new TranslatableMarkup('Operation stopped by request'); break; case MigrationInterface::RESULT_FAILED: $context['sandbox']['messages'][] = (string) new TranslatableMarkup('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'][] = (string) new TranslatableMarkup('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'][] = (string) $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'] .= '…'; } // 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 = \Drupal::service('plugin.manager.migration')->createInstance($migration_id); $migration_name = $migration->label() ? $migration->label() : $migration_id; if ($operation == 'import') { $context['message'] = (string) new TranslatableMarkup('Currently upgrading @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']; }
/** * Run a single migration batch. * * @param array $initial_ids * The full set of migration IDs to import. * @param string $operation * 'import' or 'rollback'. * @param $context * The batch context. */ public static function run($initial_ids, $operation, &$context) { if (!static::$listenersAdded) { if ($operation == 'import') { \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE, [get_class(), 'onPostRowSave']); \Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_SAVE, [get_class(), 'onMapSave']); \Drupal::service('event_dispatcher')->addListener(MigrateEvents::IDMAP_MESSAGE, [get_class(), 'onIdMapMessage']); } else { \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_DELETE, [get_class(), 'onPostRowDelete']); \Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_DELETE, [get_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: if ($operation == 'import') { $singular_message = 'Upgraded @migration (processed 1 item total)'; $plural_message = 'Upgraded @migration (processed @num_processed items total)'; } else { $singular_message = 'Rolled back @migration (processed 1 item total)'; $plural_message = 'Rolled back @migration (processed @num_processed items total)'; $migration->delete(); } // Store the number processed in the sandbox. $context['sandbox']['num_processed'] += static::$numProcessed; $message = \Drupal::translation()->formatPlural($context['sandbox']['num_processed'], $singular_message, $plural_message, ['@migration' => $migration_name, '@num_processed' => $context['sandbox']['num_processed']]); $context['sandbox']['messages'][] = $message; static::logger()->notice($message); $context['sandbox']['num_processed'] = 0; $context['results']['successes']++; break; case MigrationInterface::RESULT_INCOMPLETE: $singular_message = 'Continuing with @migration (processed 1 item)'; $plural_message = 'Continuing with @migration (processed @num_processed items)'; $context['sandbox']['messages'][] = \Drupal::translation()->formatPlural(static::$numProcessed, $singular_message, $plural_message, ['@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'] .= '…'; } // 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') { $message = 'Currently upgrading @migration (@current of @max total tasks)'; } else { $message = 'Currently rolling back @migration (@current of @max total tasks)'; } $context['message'] = t($message, ['@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']; }