From 852db7bc9d14f465d0a06b095c7050896d353d36 Mon Sep 17 00:00:00 2001 From: Claudiu Cristea Date: Tue, 4 Jan 2022 21:56:30 +0200 Subject: [PATCH] Inject migration plugin manager (#4980) * Inject migration plugin manager. * Strict typing & tidy-up * PHPCS * Don't pass the event name anymore * Fix PHP 8.1 deprecation notices * Prevent float convert PHP8 deprecation notice --- src/Drupal/Commands/core/EntityCommands.php | 2 +- .../Commands/core/MigrateRunnerCommands.php | 126 ++++++++---------- src/Drupal/Commands/core/drush.services.yml | 2 +- src/Drupal/Migrate/MigrateExecutable.php | 61 +++------ src/Drupal/Migrate/MigrateIdMapFilter.php | 8 +- .../Migrate/MigrateMissingSourceRowsEvent.php | 8 +- src/Drupal/Migrate/MigratePrepareRowEvent.php | 18 +-- src/Drupal/Migrate/MigrateUtils.php | 3 +- 8 files changed, 85 insertions(+), 143 deletions(-) diff --git a/src/Drupal/Commands/core/EntityCommands.php b/src/Drupal/Commands/core/EntityCommands.php index 4b53e4a696..38408d6617 100644 --- a/src/Drupal/Commands/core/EntityCommands.php +++ b/src/Drupal/Commands/core/EntityCommands.php @@ -169,7 +169,7 @@ protected function getQuery(string $entity_type, ?string $ids, array $options): { $storage = $this->entityTypeManager->getStorage($entity_type); $query = $storage->getQuery(); - if ($ids = StringUtils::csvToArray($ids)) { + if ($ids = StringUtils::csvToArray((string) $ids)) { $idKey = $this->entityTypeManager->getDefinition($entity_type)->getKey('id'); $query = $query->condition($idKey, $ids, 'IN'); } elseif ($options['bundle'] || $options['exclude']) { diff --git a/src/Drupal/Commands/core/MigrateRunnerCommands.php b/src/Drupal/Commands/core/MigrateRunnerCommands.php index 85443f3afd..0b2544241c 100644 --- a/src/Drupal/Commands/core/MigrateRunnerCommands.php +++ b/src/Drupal/Commands/core/MigrateRunnerCommands.php @@ -2,13 +2,13 @@ namespace Drush\Drupal\Commands\core; -use Drupal\Core\KeyValueStore\KeyValueStoreInterface; -use Drupal\Component\Plugin\Exception\PluginException; use Consolidation\AnnotatedCommand\CommandData; use Consolidation\AnnotatedCommand\CommandError; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; +use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Core\Datetime\DateFormatter; use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\MigrateMessageInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; @@ -29,31 +29,23 @@ class MigrateRunnerCommands extends DrushCommands { /** * Migration plugin manager service. - * - * @var MigrationPluginManagerInterface */ - protected $migrationPluginManager; + protected ?MigrationPluginManagerInterface $migrationPluginManager; /** * Date formatter service. - * - * @var DateFormatter */ - protected $dateFormatter; + protected DateFormatter $dateFormatter; /** * The key-value store service. - * - * @var KeyValueStoreInterface */ - protected $keyValue; + protected KeyValueStoreInterface $keyValue; /** * Migrate message service. - * - * @var MigrateMessageInterface */ - protected $migrateMessage; + protected MigrateMessageInterface $migrateMessage; /** * Constructs a new class instance. @@ -62,12 +54,15 @@ class MigrateRunnerCommands extends DrushCommands * Date formatter service. * @param KeyValueFactoryInterface $keyValueFactory * The key-value factory service. + * @param MigrationPluginManagerInterface|null $migrationPluginManager + * The migration plugin manager service. */ - public function __construct(DateFormatter $dateFormatter, KeyValueFactoryInterface $keyValueFactory) + public function __construct(DateFormatter $dateFormatter, KeyValueFactoryInterface $keyValueFactory, ?MigrationPluginManagerInterface $migrationPluginManager = null) { parent::__construct(); $this->dateFormatter = $dateFormatter; $this->keyValue = $keyValueFactory->get('migrate_last_imported'); + $this->migrationPluginManager = $migrationPluginManager; } /** @@ -230,13 +225,13 @@ protected function getMigrationSourceRowsCount(MigrationInterface $migration): ? } /** - * Returns the number or items that needs update. + * Returns the number of items that needs update. * * @param MigrationInterface $migration * The migration plugin instance. * * @return int|null - * The number or items that needs update. + * The number of items that needs update. */ protected function getMigrationNeedingUpdateCount(MigrationInterface $migration): int { @@ -297,7 +292,7 @@ protected function getMigrationImportedCount(MigrationInterface $migration): ?in protected function getMigrationLastImportedTime(MigrationInterface $migration): string { if ($lastImported = $this->keyValue->get($migration->id(), '')) { - $lastImported = $this->dateFormatter->format($lastImported / 1000, 'custom', 'Y-m-d H:i:s'); + $lastImported = $this->dateFormatter->format(round($lastImported / 1000), 'custom', 'Y-m-d H:i:s'); } return $lastImported; } @@ -436,7 +431,7 @@ protected function executeMigration(MigrationInterface $migration, string $migra // Remove already executed migrations. $dependencies = array_diff($dependencies, $executedMigrations); if ($dependencies) { - $requiredMigrations = $this->getMigrationPluginManager()->createInstances($dependencies); + $requiredMigrations = $this->migrationPluginManager->createInstances($dependencies); array_walk($requiredMigrations, [static::class, __FUNCTION__], $userData); } } @@ -546,11 +541,13 @@ public function rollback(?string $migrationIds = null, array $options = ['all' = * @validate-module-enabled migrate * @validate-migration-id * @version 10.4 + * + * @throws \Drupal\Component\Plugin\Exception\PluginException */ public function stop(string $migrationId): void { /** @var MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + $migration = $this->migrationPluginManager->createInstance($migrationId); switch ($migration->getStatus()) { case MigrationInterface::STATUS_IDLE: $this->logger()->warning(dt('Migration @id is idle', ['@id' => $migrationId])); @@ -583,11 +580,13 @@ public function stop(string $migrationId): void * @validate-module-enabled migrate * @validate-migration-id * @version 10.4 + * + * @throws \Drupal\Component\Plugin\Exception\PluginException */ public function resetStatus(string $migrationId): void { /** @var MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + $migration = $this->migrationPluginManager->createInstance($migrationId); $status = $migration->getStatus(); if ($status == MigrationInterface::STATUS_IDLE) { $this->logger()->warning(dt('Migration @id is already Idle', ['@id' => $migrationId])); @@ -605,16 +604,20 @@ public function resetStatus(string $migrationId): void * @param string $migrationId * The ID of the migration. * - * @option idlist Comma-separated list of IDs to import. As an ID may have more than one column, concatenate the columns with the colon ':' separator. + * @option idlist Comma-separated list of IDs to import. As an ID may have + * more than one column, concatenate the columns with the colon ':' + * separator. * * @usage migrate:messages article * Show all messages for the article migration * @usage migrate:messages article --idlist=5 * Show messages related to article record with source ID 5. * @usage migrate:messages node_revision --idlist=1:2,2:3,3:5 - * Show messages related to node revision records with source IDs [1,2], [2,3], and [3,5]. + * Show messages related to node revision records with source IDs [1,2], + * [2,3], and [3,5]. * @usage migrate:messages custom_node_revision --idlist=1:"r:1",2:"r:3" - * Show messages related to node revision records with source IDs [1,"r:1"], and [2,"r:3"]. + * Show messages related to node revision records with source IDs + * [1,"r:1"], and [2,"r:3"]. * * @aliases mmsg,migrate-messages * @@ -634,11 +637,13 @@ public function resetStatus(string $migrationId): void * * @return RowsOfFields * Migration messages status formatted as table. + * + * @throws \Drupal\Component\Plugin\Exception\PluginException */ public function messages(string $migrationId, array $options = ['idlist' => self::REQ]): RowsOfFields { /** @var MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + $migration = $this->migrationPluginManager->createInstance($migrationId); $idMap = $migration->getIdMap(); $sourceIdKeys = $this->getSourceIdKeys($idMap); $table = []; @@ -648,20 +653,18 @@ public function messages(string $migrationId, array $options = ['idlist' => self return new RowsOfFields($table); } if (!empty($options['idlist'])) { - // There is not way to retreive a filtered set of messages from an - // ID map on Drupal core, right now. - // Even if using \Drush\Drupal\Migrate\MigrateIdMapFilter does the - // right thing filtering the data on the ID map, sadly its - // getMessages() method does not take it into account the iterator, - // and retrieves data directly, e.g. at SQL ID map plugin. - // On the other side Drupal core's - // \Drupal\migrate\Plugin\MigrateIdMapInterface only allows to - // filter by one source IDs set, and not by multiple, on - // getMessages(). - // For now, go over known IDs passed directly, one at a time a - // work-around, at the cost of more queries in the usual SQL ID map, - // which is likely OK for its use, to show only few source IDs - // messages. + // There is no way to retrieve a filtered set of messages from an ID + // map on Drupal core, right now. Even if using + // \Drush\Drupal\Migrate\MigrateIdMapFilter does the right thing + // filtering the data on the ID map, sadly its getMessages() method + // does not take it into account the iterator, and retrieves data + // directly, e.g. at SQL ID map plugin. On the other side Drupal + // core's \Drupal\migrate\Plugin\MigrateIdMapInterface only allows + // to filter by one source IDs set, and not by multiple, on + // getMessages(). For now, go over known IDs passed directly, one at + // a time a workaround, at the cost of more queries in the usual SQL + // ID map, which is likely OK for its use, to show only few source + // IDs messages. foreach (MigrateUtils::parseIdList($options['idlist']) as $sourceIdValues) { foreach ($idMap->getMessages($sourceIdValues) as $row) { $table[] = $this->preprocessMessageRow($row, $sourceIdKeys); @@ -669,7 +672,6 @@ public function messages(string $migrationId, array $options = ['idlist' => self } return new RowsOfFields($table); } - $table = []; foreach ($idMap->getMessages() as $row) { $table[] = $this->preprocessMessageRow($row, $sourceIdKeys); } @@ -699,10 +701,10 @@ protected function preprocessMessageRow(\StdClass $row, array $sourceIdKeys): ar } $sourceIds = $destinationIds = []; foreach ($row as $key => $value) { - if (substr($key, 0, 4) === 'src_') { + if (str_starts_with($key, 'src_')) { $sourceIds[$key] = $value; } - if (substr($key, 0, 5) === 'dest_') { + if (str_starts_with($key, 'dest_')) { $destinationIds[$key] = $value; } } @@ -737,11 +739,13 @@ protected function preprocessMessageRow(\StdClass $row, array $sourceIdKeys): ar * * @return RowsOfFields * Source fields of the given migration. + * + * @throws \Drupal\Component\Plugin\Exception\PluginException */ public function fieldsSource(string $migrationId): RowsOfFields { /** @var MigrationInterface $migration */ - $migration = $this->getMigrationPluginManager()->createInstance($migrationId); + $migration = $this->migrationPluginManager->createInstance($migrationId); $source = $migration->getSourcePlugin(); $table = []; foreach ($source->fields() as $machineName => $description) { @@ -770,7 +774,7 @@ public function fieldsSource(string $migrationId): RowsOfFields protected function getMigrationList(?string $migrationIds, ?string $tags): array { $migrationIds = StringUtils::csvToArray((string) $migrationIds); - $migrations = $this->getMigrationPluginManager()->createInstances($migrationIds); + $migrations = $this->migrationPluginManager->createInstances($migrationIds); // Check for invalid migration IDs. if ($invalidMigrations = array_diff_key(array_flip($migrationIds), $migrations)) { @@ -784,7 +788,7 @@ protected function getMigrationList(?string $migrationIds, ?string $tags): array $sourcePlugin->checkRequirements(); } } catch (RequirementsException $exception) { - $this->logger()->debug("Migration '{$migrationId}' is skipped as its source plugin has missed requirements: " . $exception->getRequirementsString()); + $this->logger()->debug("Migration '$migrationId' is skipped as its source plugin has missed requirements: {$exception->getRequirementsString()}"); unset($migrations[$migrationId]); } } @@ -798,7 +802,7 @@ protected function getMigrationList(?string $migrationIds, ?string $tags): array $list = []; foreach ($migrations as $migrationId => $migration) { - $migrationTags = (array)$migration->getMigrationTags(); + $migrationTags = $migration->getMigrationTags(); $commonTags = array_intersect($tags, $migrationTags); if (!$commonTags) { // Skip if migration is not tagged with any of the passed tags. @@ -827,29 +831,6 @@ protected function getMigrateMessage(): MigrateMessageInterface return $this->migrateMessage; } - /** - * Returns the migration plugin manager service. - * - * @return MigrationPluginManagerInterface - * The migration plugin manager service. - * - * @todo This service cannot be injected as the 'migrate' module might not - * be enabled and will throw the following exception: - * > Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException - * > The service "migrate_runner.commands" has a dependency on a - * > non-existent service "plugin.manager.migration". - * Unfortunately, we cannot avoid the class instantiation, via an - * annotation (as @validate-module-enabled for methods), if a specific - * module is not installed. Open a followup to tackle this issue. - */ - protected function getMigrationPluginManager(): MigrationPluginManagerInterface - { - if (!isset($this->migrationPluginManager)) { - $this->migrationPluginManager = \Drupal::service('plugin.manager.migration'); - } - return $this->migrationPluginManager; - } - /** * Get the source ID keys. * @@ -869,7 +850,7 @@ protected function getSourceIdKeys(MigrateIdMapInterface $idMap): array $idMap->rewind(); $columns = $idMap->currentSource(); $sourceIdKeys = array_map(static function ($id) { - return "src_{$id}"; + return "src_$id"; }, array_keys($columns)); return array_combine($sourceIdKeys, $sourceIdKeys); } @@ -886,12 +867,13 @@ protected function getSourceIdKeys(MigrateIdMapInterface $idMap): array * * @return CommandError|null */ - public function validateMigrationId(CommandData $commandData) + public function validateMigrationId(CommandData $commandData): ?CommandError { $argName = $commandData->annotationData()->get('validate-migration-id') ?: 'migrationId'; $migrationId = $commandData->input()->getArgument($argName); - if (!$this->getMigrationPluginManager()->hasDefinition($migrationId)) { + if (!$this->migrationPluginManager->hasDefinition($migrationId)) { return new CommandError(dt('Migration "@id" does not exist', ['@id' => $migrationId])); } + return null; } } diff --git a/src/Drupal/Commands/core/drush.services.yml b/src/Drupal/Commands/core/drush.services.yml index 915e5e1232..597a732759 100644 --- a/src/Drupal/Commands/core/drush.services.yml +++ b/src/Drupal/Commands/core/drush.services.yml @@ -52,7 +52,7 @@ services: - { name: drush.command } migrate_runner.commands: class: Drush\Drupal\Commands\core\MigrateRunnerCommands - arguments: ['@date.formatter', '@keyvalue'] + arguments: ['@date.formatter', '@keyvalue', '@?plugin.manager.migration'] tags: - { name: drush.command } queue.commands: diff --git a/src/Drupal/Migrate/MigrateExecutable.php b/src/Drupal/Migrate/MigrateExecutable.php index 94f07dd52d..00f027bf84 100644 --- a/src/Drupal/Migrate/MigrateExecutable.php +++ b/src/Drupal/Migrate/MigrateExecutable.php @@ -25,18 +25,13 @@ class MigrateExecutable extends MigrateExecutableBase { /** * The Symfony console output. - * - * @var OutputInterface */ - protected $output; + protected OutputInterface $output; /** * Counters of map statuses. - * - * @var array - * Set of counters, keyed by MigrateIdMapInterface::STATUS_* constant. */ - protected $saveCounters = [ + protected array $saveCounters = [ MigrateIdMapInterface::STATUS_FAILED => 0, MigrateIdMapInterface::STATUS_IGNORED => 0, MigrateIdMapInterface::STATUS_IMPORTED => 0, @@ -45,10 +40,8 @@ class MigrateExecutable extends MigrateExecutableBase /** * Counter of map deletions. - * - * @var int */ - protected $deleteCounter = 0; + protected int $deleteCounter = 0; /** * Maximum number of items to process in this migration. @@ -66,80 +59,60 @@ class MigrateExecutable extends MigrateExecutableBase /** * Show timestamp in progress message. - * - * @var bool */ - protected $showTimestamp; + protected bool $showTimestamp; /** * Show internal counter in progress message. - * - * @var bool */ - protected $showTotal; + protected bool $showTotal; /** * List of specific source IDs to import. - * - * @var array */ - protected $idlist; + protected array $idlist; /** * List of all source IDs that are found in source during this migration. - * - * @var array */ - protected $allSourceIdValues = []; + protected array $allSourceIdValues = []; /** * Count of number of items processed so far in this migration. - * - * @var int */ - protected $counter = 0; + protected int $counter = 0; /** * Whether the destination item exists before saving. - * - * @var bool */ - protected $preExistingItem = false; + protected bool $preExistingItem = false; /** * List of event listeners we have registered. * * @var callable[] */ - protected $listeners = []; + protected array $listeners = []; /** * Whether to delete rows missing from source after an import. - * - * @var bool */ - protected $deleteMissingSourceRows; + protected bool $deleteMissingSourceRows; /** * Static cached ID map. - * - * @var MigrateIdMapFilter */ - protected $idMap; + protected ?MigrateIdMapFilter $idMap; /** - * If the execution exposes an progress bar. - * - * @var bool + * If the execution exposes a progress bar. */ - protected $exposeProgressBar; + protected bool $exposeProgressBar; /** * The Symfony progress bar. - * - * @var ProgressBar */ - protected $progressBar; + protected ?ProgressBar $progressBar; /** * Constructs a new migrate executable instance. @@ -185,7 +158,7 @@ public function __construct(MigrationInterface $migration, MigrateMessageInterfa // Cannot use the progress bar when: // - `--no-progress` option is used, // - `--feedback` option is used, - // - The migration source plugin is configured to skips count. + // - The migration source plugin is configured to skip count. $this->exposeProgressBar = $options['progress'] && !$this->feedback && empty($migration->getSourceConfiguration()['skip_count']); $this->listeners[MigrateEvents::MAP_SAVE] = [$this, 'onMapSave']; @@ -286,7 +259,7 @@ protected function handleMissingSourceRows(MigrationInterface $migration): void if ($destinationIds) { $missingSourceEvent = new MigrateMissingSourceRowsEvent($migration, $destinationIds); - $this->getEventDispatcher()->dispatch(MigrateMissingSourceRowsEvent::class, $missingSourceEvent); + $this->getEventDispatcher()->dispatch($missingSourceEvent); } } diff --git a/src/Drupal/Migrate/MigrateIdMapFilter.php b/src/Drupal/Migrate/MigrateIdMapFilter.php index ee12bc8c37..81e9863b7b 100644 --- a/src/Drupal/Migrate/MigrateIdMapFilter.php +++ b/src/Drupal/Migrate/MigrateIdMapFilter.php @@ -12,17 +12,13 @@ class MigrateIdMapFilter extends \FilterIterator /** * List of specific source IDs to filter on. - * - * @var array */ - protected $sourceIdList; + protected array $sourceIdList; /** * List of specific destination IDs to filter on. - * - * @var array */ - protected $destinationIdList; + protected array $destinationIdList; /** * @param MigrateIdMapInterface $idMap diff --git a/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php b/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php index 1054d34a12..58d4e452d0 100644 --- a/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php +++ b/src/Drupal/Migrate/MigrateMissingSourceRowsEvent.php @@ -3,7 +3,7 @@ namespace Drush\Drupal\Migrate; use Drupal\migrate\Plugin\MigrationInterface; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * Missing source rows event. @@ -12,17 +12,15 @@ class MigrateMissingSourceRowsEvent extends Event { /** * The migration plugin instance. - * - * @var MigrationInterface */ - protected $migration; + protected MigrationInterface $migration; /** * Values representing the destination IDs. * * @var array[] */ - protected $destinationIds; + protected array $destinationIds; /** * Constructs a new event instance. diff --git a/src/Drupal/Migrate/MigratePrepareRowEvent.php b/src/Drupal/Migrate/MigratePrepareRowEvent.php index fa6c1e259f..6ec7d57563 100644 --- a/src/Drupal/Migrate/MigratePrepareRowEvent.php +++ b/src/Drupal/Migrate/MigratePrepareRowEvent.php @@ -5,7 +5,7 @@ use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\MigrateSourceInterface; use Drupal\migrate\Row; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * Wraps a prepare-row event for event listeners. @@ -19,24 +19,18 @@ final class MigratePrepareRowEvent extends Event { /** * Row object. - * - * @var Row */ - protected $row; + protected Row $row; /** * Migration source plugin. - * - * @var MigrateSourceInterface */ - protected $source; + protected MigrateSourceInterface $source; /** * Migration plugin. - * - * @var MigrationInterface */ - protected $migration; + protected MigrationInterface $migration; /** * Constructs a prepare-row event object. @@ -69,8 +63,8 @@ public function getRow(): Row /** * Gets the source plugin. * - * @return MigrateSourceInterface $source - The source plugin firing the event. + * @return MigrateSourceInterface $source + * The source plugin firing the event. */ public function getSource(): MigrateSourceInterface { diff --git a/src/Drupal/Migrate/MigrateUtils.php b/src/Drupal/Migrate/MigrateUtils.php index 90744a3e0b..fa2259ecb4 100644 --- a/src/Drupal/Migrate/MigrateUtils.php +++ b/src/Drupal/Migrate/MigrateUtils.php @@ -7,12 +7,11 @@ */ class MigrateUtils { - /** * Parses as an array the list of IDs received from console. * * IDs are delimited by comma. Each ID consists in one are many ID columns, - * separated by a colon (:). + * separated by a colon (":"). * * @param string|null $idlist *