array( * // An array of migration IDs that must be run before this migration. * ), * 'optional' => array( * // An array of migration IDs that, if they exist, must be run before * // this migration. * ), * ); * @endcode * * @var array */ protected $migration_dependencies = []; /** * The migration's configuration dependencies. * * These store any dependencies on modules or other configuration (including * other migrations) that must be available before the migration can be * created. * * @see \Drupal\Core\Config\Entity\ConfigDependencyManager * * @var array */ protected $dependencies = []; /** * The migration plugin manager for loading other migration plugins. * * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface */ protected $migrationPluginManager; /** * The source plugin manager. * * @var \Drupal\migrate\Plugin\MigratePluginManager */ protected $sourcePluginManager; /** * Thep process plugin manager. * * @var \Drupal\migrate\Plugin\MigratePluginManager */ protected $processPluginManager; /** * The destination plugin manager. * * @var \Drupal\migrate\Plugin\MigrateDestinationPluginManager */ protected $destinationPluginManager; /** * The ID map plugin manager. * * @var \Drupal\migrate\Plugin\MigratePluginManager */ protected $idMapPluginManager; /** * Labels corresponding to each defined status. * * @var array */ protected $statusLabels = [ self::STATUS_IDLE => 'Idle', self::STATUS_IMPORTING => 'Importing', self::STATUS_ROLLING_BACK => 'Rolling back', self::STATUS_STOPPING => 'Stopping', self::STATUS_DISABLED => 'Disabled', ]; /** * Constructs a Migration. * * @param array $configuration * Plugin configuration. * @param string $plugin_id * The plugin ID. * @param mixed $plugin_definition * The plugin definition. * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager * The migration plugin manager. * @param \Drupal\migrate\Plugin\MigratePluginManagerInterface $source_plugin_manager * The source migration plugin manager. * @param \Drupal\migrate\Plugin\MigratePluginManagerInterface $process_plugin_manager * The process migration plugin manager. * @param \Drupal\migrate\Plugin\MigrateDestinationPluginManager $destination_plugin_manager * The destination migration plugin manager. * @param \Drupal\migrate\Plugin\MigratePluginManagerInterface $idmap_plugin_manager * The ID map migration plugin manager. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManagerInterface $source_plugin_manager, MigratePluginManagerInterface $process_plugin_manager, MigrateDestinationPluginManager $destination_plugin_manager, MigratePluginManagerInterface $idmap_plugin_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->migrationPluginManager = $migration_plugin_manager; $this->sourcePluginManager = $source_plugin_manager; $this->processPluginManager = $process_plugin_manager; $this->destinationPluginManager = $destination_plugin_manager; $this->idMapPluginManager = $idmap_plugin_manager; foreach (NestedArray::mergeDeep($plugin_definition, $configuration) as $key => $value) { $this->$key = $value; } } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $configuration, $plugin_id, $plugin_definition, $container->get('plugin.manager.migration'), $container->get('plugin.manager.migrate.source'), $container->get('plugin.manager.migrate.process'), $container->get('plugin.manager.migrate.destination'), $container->get('plugin.manager.migrate.id_map') ); } /** * {@inheritdoc} */ public function id() { return $this->pluginId; } /** * {@inheritdoc} */ public function label() { return $this->label; } /** * Gets any arbitrary property's value. * * @param string $property * The property to retrieve. * * @return mixed * The value for that property, or NULL if the property does not exist. * * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.x. Use * more specific getters instead. */ public function get($property) { return isset($this->$property) ? $this->$property : NULL; } /** * Retrieves the ID map plugin. * * @return \Drupal\migrate\Plugin\MigrateIdMapInterface * The ID map plugin. */ public function getIdMapPlugin() { return $this->idMapPlugin; } /** * {@inheritdoc} */ public function getSourcePlugin() { if (!isset($this->sourcePlugin)) { $this->sourcePlugin = $this->sourcePluginManager->createInstance($this->source['plugin'], $this->source, $this); } return $this->sourcePlugin; } /** * {@inheritdoc} */ public function getProcessPlugins(array $process = NULL) { if (!isset($process)) { $process = $this->getProcess(); } $index = serialize($process); if (!isset($this->processPlugins[$index])) { $this->processPlugins[$index] = []; foreach ($this->getProcessNormalized($process) as $property => $configurations) { $this->processPlugins[$index][$property] = []; foreach ($configurations as $configuration) { if (isset($configuration['source'])) { $this->processPlugins[$index][$property][] = $this->processPluginManager->createInstance('get', $configuration, $this); } // Get is already handled. if ($configuration['plugin'] != 'get') { $this->processPlugins[$index][$property][] = $this->processPluginManager->createInstance($configuration['plugin'], $configuration, $this); } if (!$this->processPlugins[$index][$property]) { throw new MigrateException("Invalid process configuration for $property"); } } } } return $this->processPlugins[$index]; } /** * Resolve shorthands into a list of plugin configurations. * * @param array $process * A process configuration array. * * @return array * The normalized process configuration. */ protected function getProcessNormalized(array $process) { $normalized_configurations = []; foreach ($process as $destination => $configuration) { if (is_string($configuration)) { $configuration = [ 'plugin' => 'get', 'source' => $configuration, ]; } if (isset($configuration['plugin'])) { $configuration = [$configuration]; } $normalized_configurations[$destination] = $configuration; } return $normalized_configurations; } /** * {@inheritdoc} */ public function getDestinationPlugin($stub_being_requested = FALSE) { if ($stub_being_requested && !empty($this->destination['no_stub'])) { throw new MigrateSkipRowException(); } if (!isset($this->destinationPlugin)) { $this->destinationPlugin = $this->destinationPluginManager->createInstance($this->destination['plugin'], $this->destination, $this); } return $this->destinationPlugin; } /** * {@inheritdoc} */ public function getIdMap() { if (!isset($this->idMapPlugin)) { $configuration = $this->idMap; $plugin = isset($configuration['plugin']) ? $configuration['plugin'] : 'sql'; $this->idMapPlugin = $this->idMapPluginManager->createInstance($plugin, $configuration, $this); } return $this->idMapPlugin; } /** * {@inheritdoc} */ public function checkRequirements() { // Check whether the current migration source and destination plugin // requirements are met or not. if ($this->getSourcePlugin() instanceof RequirementsInterface) { $this->getSourcePlugin()->checkRequirements(); } if ($this->getDestinationPlugin() instanceof RequirementsInterface) { $this->getDestinationPlugin()->checkRequirements(); } if (empty($this->requirements)) { // There are no requirements to check. return; } /** @var \Drupal\migrate\Plugin\MigrationInterface[] $required_migrations */ $required_migrations = $this->getMigrationPluginManager()->createInstances($this->requirements); $missing_migrations = array_diff($this->requirements, array_keys($required_migrations)); // Check if the dependencies are in good shape. foreach ($required_migrations as $migration_id => $required_migration) { if (!$required_migration->allRowsProcessed()) { $missing_migrations[] = $migration_id; } } if ($missing_migrations) { throw new RequirementsException('Missing migrations ' . implode(', ', $missing_migrations) . '.', ['requirements' => $missing_migrations]); } } /** * Gets the migration plugin manager. * * @return \Drupal\migrate\Plugin\MigratePluginManager * The plugin manager. */ protected function getMigrationPluginManager() { return $this->migrationPluginManager; } /** * {@inheritdoc} */ public function setStatus($status) { \Drupal::keyValue('migrate_status')->set($this->id(), $status); } /** * {@inheritdoc} */ public function getStatus() { return \Drupal::keyValue('migrate_status')->get($this->id(), static::STATUS_IDLE); } /** * {@inheritdoc} */ public function getStatusLabel() { $status = $this->getStatus(); if (isset($this->statusLabels[$status])) { return $this->statusLabels[$status]; } else { return ''; } } /** * {@inheritdoc} */ public function getInterruptionResult() { return \Drupal::keyValue('migrate_interruption_result')->get($this->id(), static::RESULT_INCOMPLETE); } /** * {@inheritdoc} */ public function clearInterruptionResult() { \Drupal::keyValue('migrate_interruption_result')->delete($this->id()); } /** * {@inheritdoc} */ public function interruptMigration($result) { $this->setStatus(MigrationInterface::STATUS_STOPPING); \Drupal::keyValue('migrate_interruption_result')->set($this->id(), $result); } /** * {@inheritdoc} */ public function allRowsProcessed() { $source_count = $this->getSourcePlugin()->count(); // If the source is uncountable, we have no way of knowing if it's // complete, so stipulate that it is. if ($source_count < 0) { return TRUE; } $processed_count = $this->getIdMap()->processedCount(); // We don't use == because in some circumstances (like unresolved stubs // being created), the processed count may be higher than the available // source rows. return $source_count <= $processed_count; } /** * {@inheritdoc} */ public function set($property_name, $value) { if ($property_name == 'source') { // Invalidate the source plugin. unset($this->sourcePlugin); } elseif ($property_name === 'destination') { // Invalidate the destination plugin. unset($this->destinationPlugin); } $this->{$property_name} = $value; return $this; } /** * {@inheritdoc} */ public function getProcess() { return $this->getProcessNormalized($this->process); } /** * {@inheritdoc} */ public function setProcess(array $process) { $this->process = $process; return $this; } /** * {@inheritdoc} */ public function setProcessOfProperty($property, $process_of_property) { $this->process[$property] = $process_of_property; return $this; } /** * {@inheritdoc} */ public function mergeProcessOfProperty($property, array $process_of_property) { // If we already have a process value then merge the incoming process array //otherwise simply set it. $current_process = $this->getProcess(); if (isset($current_process[$property])) { $this->process = NestedArray::mergeDeepArray([$current_process, $this->getProcessNormalized([$property => $process_of_property])], TRUE); } else { $this->setProcessOfProperty($property, $process_of_property); } return $this; } /** * {@inheritdoc} */ public function isTrackLastImported() { return $this->trackLastImported; } /** * {@inheritdoc} */ public function setTrackLastImported($track_last_imported) { $this->trackLastImported = (bool) $track_last_imported; return $this; } /** * {@inheritdoc} */ public function getMigrationDependencies() { $this->migration_dependencies = ($this->migration_dependencies ?: []) + ['required' => [], 'optional' => []]; $this->migration_dependencies['optional'] = array_unique(array_merge($this->migration_dependencies['optional'], $this->findMigrationDependencies($this->process))); return $this->migration_dependencies; } /** * Find migration dependencies from the migration and the iterator plugins. * * @param $process * @return array */ protected function findMigrationDependencies($process) { $return = []; foreach ($this->getProcessNormalized($process) as $process_pipeline) { foreach ($process_pipeline as $plugin_configuration) { if ($plugin_configuration['plugin'] == 'migration') { $return = array_merge($return, (array) $plugin_configuration['migration']); } if ($plugin_configuration['plugin'] == 'iterator') { $return = array_merge($return, $this->findMigrationDependencies($plugin_configuration['process'])); } } } return $return; } /** * {@inheritdoc} */ public function getPluginDefinition() { $definition = []; // While normal plugins do not change their definitions on the fly, this // one does so accommodate for that. foreach (parent::getPluginDefinition() as $key => $value) { $definition[$key] = isset($this->$key) ? $this->$key : $value; } return $definition; } /** * {@inheritdoc} */ public function getDestinationConfiguration() { return $this->destination; } /** * {@inheritdoc} */ public function getSourceConfiguration() { return $this->source; } /** * {@inheritdoc} */ public function getTrackLastImported() { return $this->trackLastImported; } /** * {@inheritdoc} */ public function getDestinationIds() { return $this->destinationIds; } /** * {@inheritdoc} */ public function getMigrationTags() { return $this->migration_tags; } }