
| Current Path : /var/www/html/vendor/drush/drush/src/Drupal/Commands/core/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/html/vendor/drush/drush/src/Drupal/Commands/core/MigrateRunnerCommands.php |
<?php
namespace Drush\Drupal\Commands\core;
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\AnnotatedCommand\CommandError;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
use Drupal\migrate\Exception\RequirementsException;
use Drupal\migrate\MigrateMessageInterface;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Drupal\migrate\Plugin\RequirementsInterface;
use Drush\Commands\DrushCommands;
use Drush\Drupal\Migrate\MigrateExecutable;
use Drush\Drupal\Migrate\MigrateMessage;
use Drush\Drupal\Migrate\MigrateUtils;
use Drush\Utils\StringUtils;
use Symfony\Component\Filesystem\Path;
/**
* Migrate runner commands.
*/
class MigrateRunnerCommands extends DrushCommands
{
/**
* Migration plugin manager service.
*/
protected ?MigrationPluginManagerInterface $migrationPluginManager;
/**
* Date formatter service.
*/
protected DateFormatterInterface $dateFormatter;
/**
* The key-value store service.
*/
protected KeyValueStoreInterface $keyValue;
/**
* Migrate message service.
*/
protected MigrateMessageInterface $migrateMessage;
/**
* Constructs a new class instance.
*
* @param DateFormatterInterface $dateFormatter
* Date formatter service.
* @param KeyValueFactoryInterface $keyValueFactory
* The key-value factory service.
* @param MigrationPluginManagerInterface|null $migrationPluginManager
* The migration plugin manager service.
*/
public function __construct(DateFormatterInterface $dateFormatter, KeyValueFactoryInterface $keyValueFactory, ?MigrationPluginManagerInterface $migrationPluginManager = null)
{
parent::__construct();
$this->dateFormatter = $dateFormatter;
$this->keyValue = $keyValueFactory->get('migrate_last_imported');
$this->migrationPluginManager = $migrationPluginManager;
}
/**
* List all migrations with current status.
*
* @command migrate:status
*
* @param string|null $migrationIds
* Restrict to a comma-separated list of migrations. Optional.
*
* @option tag A comma-separated list of migration tags to list. If only
* <info>--tag</info> is provided, all tagged migrations will be listed,
* grouped by tags.
* @option names-only [Deprecated, use --field=id instead] Only return names, not all the details (faster).
*
* @usage migrate:status
* Retrieve status for all migrations
* @usage migrate:status --tag
* Retrieve status for all migrations, grouped by tag
* @usage migrate:status --tag=user,main_content
* Retrieve status for all migrations tagged with <info>user</info> or
* <info>main_content</info>
* @usage migrate:status classification,article
* Retrieve status for specific migrations
* @usage migrate:status --field=id
* Retrieve a raw list of migration IDs.
* @usage ms --fields=id,status --format=json
* Retrieve a Json serialized list of migrations, each item containing only the migration ID and its status.
*
* @aliases ms,migrate-status
*
* @topics docs:migrate
*
* @validate-module-enabled migrate
*
* @field-labels
* id: Migration ID
* status: Status
* total: Total
* imported: Imported
* needing_update: Needing update
* unprocessed: Unprocessed
* last_imported: Last Imported
* @default-fields id,status,total,imported,unprocessed,last_imported
* @filter-default-field status
* @return RowsOfFields
* Migrations status formatted as table.
* @version 10.4
*
* @throws \Exception
* If --names-only is used with --field having other value than 'id'.
*/
public function status(?string $migrationIds = null, array $options = [
'tag' => self::REQ,
'names-only' => false,
]): RowsOfFields
{
// The --names-only option takes precedence over --fields.
if ($options['names-only']) {
if ($options['field'] && $options['field'] !== 'id') {
throw new \Exception("Cannot use --names-only with --field={$options['field']}.");
}
$deprecationMessage = 'The --names-only option is deprecated in Drush 10.5.1 and is removed from Drush 11.0.0. Use --field=id instead.';
$this->logger()->warning($deprecationMessage);
@trigger_error($deprecationMessage, E_USER_DEPRECATED);
$fields = ['id'];
} elseif ($options['field']) {
$fields = [$options['field']];
} elseif ($options['fields']) {
$fields = StringUtils::csvToArray($options['fields']);
}
$list = $this->getMigrationList($migrationIds, $options['tag']);
$table = [];
// Take it one tag at a time, listing the migrations within each tag.
foreach ($list as $tag => $migrations) {
if ($tag) {
$table[] = $this->padTableRow([
'id' => dt('Tag: @name', ['@name' => $tag])
], $fields);
}
ksort($migrations);
foreach ($migrations as $migration) {
$row = [];
foreach ($fields as $field) {
switch ($field) {
case 'id':
$row[$field] = ($tag ? ' ' : '') . $migration->id();
break;
case 'status':
$row[$field] = $migration->getStatusLabel();
break;
case 'total':
$sourceRowsCount = $this->getMigrationSourceRowsCount($migration);
$row[$field] = $sourceRowsCount !== null ? $sourceRowsCount : dt('N/A');
break;
case 'needing_update':
$row[$field] = $this->getMigrationNeedingUpdateCount($migration);
break;
case 'unprocessed':
$unprocessedCount = $this->getMigrationUnprocessedCount($migration);
$row[$field] = $unprocessedCount !== null ? $unprocessedCount : dt('N/A');
break;
case 'imported':
$importedCount = $this->getMigrationImportedCount($migration);
if ($importedCount === null) {
// Next migration.
continue 2;
}
$sourceRowsCount = $sourceRowsCount ?? $this->getMigrationSourceRowsCount($migration);
if ($sourceRowsCount > 0 && $importedCount > 0) {
$importedCount .= ' (' . round(($importedCount / $sourceRowsCount) * 100, 1) . '%)';
}
$row[$field] = $importedCount;
break;
case 'last_imported':
$row[$field] = $this->getMigrationLastImportedTime($migration);
break;
}
}
$table[] = $row;
}
// Add an empty row after a tag group.
if ($tag) {
$table[] = $this->padTableRow([], $fields);
}
}
return new RowsOfFields($table);
}
/**
* Returns the migration source rows count.
*
* @param MigrationInterface $migration
* The migration plugin instance.
* @return int|null
* The migration source rows count or null if the source is uncountable or
* the source count couldn't be retrieved.
*/
protected function getMigrationSourceRowsCount(MigrationInterface $migration): ?int
{
try {
$sourceRowsCount = $migration->getSourcePlugin()->count();
// -1 indicates uncountable sources.
if ($sourceRowsCount === -1) {
return null;
}
return $sourceRowsCount;
} catch (\Exception $exception) {
$arguments = [
'@migration' => $migration->id(),
'@message' => $exception->getMessage(),
];
$this->logger()->error(dt('Could not retrieve source count from @migration: @message', $arguments));
return null;
}
}
/**
* Returns the number of items that needs update.
*
* @param MigrationInterface $migration
* The migration plugin instance.
*
* @return int|null
* The number of items that needs update.
*/
protected function getMigrationNeedingUpdateCount(MigrationInterface $migration): int
{
$map = $migration->getIdMap();
return count($map->getRowsNeedingUpdate($map->processedCount()));
}
/**
* Returns the number of unprocessed items.
*
* @param MigrationInterface $migration
* The migration plugin instance.
*
* @return int|null
* The number of unprocessed items or null if it cannot be determined.
*/
protected function getMigrationUnprocessedCount(MigrationInterface $migration): ?int
{
$sourceRowsCount = $this->getMigrationSourceRowsCount($migration);
if ($sourceRowsCount === null) {
return null;
}
return $sourceRowsCount - $migration->getIdMap()->processedCount();
}
/**
* Returns the number of imported items.
*
* @param MigrationInterface $migration
* The migration plugin instance.
*
* @return int|null
* The number of imported items or null if it cannot be determined.
*/
protected function getMigrationImportedCount(MigrationInterface $migration): ?int
{
try {
return $migration->getIdMap()->importedCount();
} catch (\Exception $exception) {
$arguments = [
'@migration' => $migration->id(),
'@message' => $exception->getMessage(),
];
$this->logger()->error(dt('Failure retrieving information on @migration: @message', $arguments));
return null;
}
}
/**
* Returns the last imported date/time if any.
*
* @param MigrationInterface $migration
* The migration plugin instance.
*
* @return string
* The last imported date/time if any.
*/
protected function getMigrationLastImportedTime(MigrationInterface $migration): string
{
if ($lastImported = $this->keyValue->get($migration->id(), '')) {
$lastImported = $this->dateFormatter->format(round($lastImported / 1000), 'custom', 'Y-m-d H:i:s');
}
return $lastImported;
}
/**
* Pads an incomplete table row with empty cells.
*
* @param array $row
* The row to be prepared.
* @param array $fields
* The table columns.
*
* @return array
* The complete table row.
*/
protected function padTableRow(array $row, array $fields): array
{
foreach (array_diff_key(array_flip($fields), $row) as $field => $delta) {
$row[$field] = null;
}
return $row;
}
/**
* Perform one or more migration processes.
*
* @command migrate:import
*
* @param string|null $migrationIds
* Comma-separated list of migration IDs.
*
* @option all Process all migrations.
* @option tag A comma-separated list of migration tags to import
* @option limit Limit on the number of items to process in each migration
* @option feedback Frequency of progress messages, in items processed
* @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 update In addition to processing unprocessed items from the source, update previously-imported items with the current data
* @option force Force an operation to run, even if all dependencies are not satisfied
* @option execute-dependencies Execute all dependent migrations first.
* @option timestamp Show progress ending timestamp in progress messages
* @option total Show total processed item number in progress messages
* @option progress Show progress bar
* @option delete Delete destination records missed from the source. Not compatible with --limit and --idlist options, and high_water_property source configuration key.
*
* @usage migrate:import --all
* Perform all migrations
* @usage migrate:import --all --no-progress
* Perform all migrations but avoid the progress bar
* @usage migrate:import --tag=user,main_content
* Import all migrations tagged with <info>user</info> and <info>main_content</info> tags
* @usage migrate:import classification,article
* Import new terms and nodes using migration <info>classification</info> and <info>article</info>
* @usage migrate:import user --limit=2
* Import no more than 2 users using the <info>user</info> migration
* @usage migrate:import user --idlist=5
* Import the user record with source ID 5
* @usage migrate:import node_revision --idlist=1:2,2:3,3:5
* Import the node revision record with source IDs [1,2], [2,3], and [3,5]
* @usage migrate:import user --limit=50 --feedback=20
* Import 50 users and show process message every 20th record
* @usage migrate:import --all --delete
* Perform all migrations and delete the destination items that are missing from source
*
* @aliases mim,migrate-import
*
* @topics docs:migrate
*
* @validate-module-enabled migrate
*
* @version 10.4
*
* @throws \Exception
* When not enough options were provided or no migration was found.
*/
public function import(?string $migrationIds = null, array $options = ['all' => false, 'tag' => self::REQ, 'limit' => self::REQ, 'feedback' => self::REQ, 'idlist' => self::REQ, 'update' => false, 'force' => false, 'execute-dependencies' => false, 'timestamp' => false, 'total' => false, 'progress' => true, 'delete' => false]): void
{
$tags = $options['tag'];
$all = $options['all'];
if (!$all && !$migrationIds && !$tags) {
throw new \Exception(dt('You must specify --all, --tag or one or more migration names separated by commas'));
}
if (!$list = $this->getMigrationList($migrationIds, $options['tag'])) {
throw new \Exception(dt('No migrations found.'));
}
$userData = [
'options' => array_intersect_key($options, array_flip([
'limit',
'feedback',
'idlist',
'update',
'force',
'timestamp',
'total',
'progress',
'delete',
])),
'execute_dependencies' => $options['execute-dependencies'],
];
// Include the file providing a migrate_prepare_row hook implementation.
require_once Path::join(DRUSH_BASE_PATH, 'src/Drupal/Migrate/migrate_runner.inc');
// If the 'migrate_prepare_row' hook implementations are already cached,
// make sure that system_migrate_prepare_row() is picked-up.
\Drupal::moduleHandler()->resetImplementations();
foreach ($list as $migrations) {
array_walk($migrations, [static::class, 'executeMigration'], $userData);
}
}
/**
* Executes a single migration.
*
* If the --execute-dependencies option was given, the migration's
* dependencies will also be executed first.
*
* @param MigrationInterface $migration
* The migration to execute.
* @param string $migrationId
* The migration ID (not used, just an artifact of array_walk()).
* @param array $userData
* Additional data passed to the callback.
*
* @throws \Exception
* If there are failed migrations.
*/
protected function executeMigration(MigrationInterface $migration, string $migrationId, array $userData): void
{
static $executedMigrations = [];
if ($userData['execute_dependencies']) {
$dependencies = $migration->getMigrationDependencies()['required'];
// Remove already executed migrations.
$dependencies = array_diff($dependencies, $executedMigrations);
if ($dependencies) {
$requiredMigrations = $this->migrationPluginManager->createInstances($dependencies);
array_walk($requiredMigrations, [static::class, __FUNCTION__], $userData);
}
}
if (!empty($userData['options']['force'])) {
$migration->set('requirements', []);
}
if (!empty($userData['options']['update'])) {
if (empty($userData['options']['idlist'])) {
$migration->getIdMap()->prepareUpdate();
} else {
$sourceIdValuesList = MigrateUtils::parseIdList($userData['options']['idlist']);
$keys = array_keys($migration->getSourcePlugin()->getIds());
foreach ($sourceIdValuesList as $sourceIdValues) {
$migration->getIdMap()->setUpdate(array_combine($keys, $sourceIdValues));
}
}
}
$executable = new MigrateExecutable($migration, $this->getMigrateMessage(), $this->output(), $userData['options']);
// drush_op() provides --simulate support.
drush_op([$executable, 'import']);
if ($count = $executable->getFailedCount()) {
// Nudge Drush to use a non-zero exit code.
throw new \Exception(dt('!name migration: !count failed.', ['!name' => $migrationId, '!count' => $count]));
}
// Keep track of executed migrations.
$executedMigrations[] = $migrationId;
}
/**
* Rollback one or more migrations.
*
* @command migrate:rollback
*
* @param string|null $migrationIds
* Comma-separated list of migration IDs.
*
* @option all Process all migrations.
* @option tag A comma-separated list of migration tags to rollback
* @option feedback Frequency of progress messages, in items processed
* @option idlist Comma-separated list of IDs to rollback. As an ID may have more than one column, concatenate the columns with the colon ':' separator
* @option progress Show progress bar
*
* @usage migrate:rollback --all
* Rollback all migrations
* @usage migrate:rollback --all --no-progress
* Rollback all migrations but avoid the progress bar
* @usage migrate:rollback --tag=user,main_content
* Rollback all migrations tagged with <info>user</info> and <info>main_content</info> tags
* @usage migrate:rollback classification,article
* Rollback terms and nodes imported by <info>classification</info> and <info>article</info> migrations
* @usage migrate:rollback user --idlist=5
* Rollback imported user record with source ID 5
*
* @aliases mr,migrate-rollback
*
* @topics docs:migrate
*
* @validate-module-enabled migrate
*
* @version 10.4
*
* @throws \Exception
* When not enough options were provided.
*/
public function rollback(?string $migrationIds = null, array $options = ['all' => false, 'tag' => self::REQ, 'feedback' => self::REQ, 'idlist' => self::REQ, 'progress' => true]): void
{
$tags = $options['tag'];
$all = $options['all'];
if (!$all && !$migrationIds && !$tags) {
throw new \Exception(dt('You must specify --all, --tag, or one or more migration names separated by commas'));
}
if (!$list = $this->getMigrationList($migrationIds, $options['tag'])) {
$this->logger()->error(dt('No migrations found.'));
}
$executableOptions = array_intersect_key(
$options,
array_flip(['feedback', 'idlist', 'progress'])
);
foreach ($list as $migrations) {
// Rollback in reverse order.
$migrations = array_reverse($migrations);
foreach ($migrations as $migration) {
$executable = new MigrateExecutable($migration, $this->getMigrateMessage(), $this->output(), $executableOptions);
// drush_op() provides --simulate support.
drush_op([$executable, 'rollback']);
}
}
}
/**
* Stop an active migration operation.
*
* @command migrate:stop
*
* @param string $migrationId
* The ID of migration to stop.
*
* @aliases mst,migrate-stop
*
* @topics docs:migrate
*
* @validate-module-enabled migrate
* @validate-migration-id
* @version 10.4
*
* @throws PluginException
*/
public function stop(string $migrationId): void
{
/** @var MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance($migrationId);
switch ($migration->getStatus()) {
case MigrationInterface::STATUS_IDLE:
$this->logger()->warning(dt('Migration @id is idle', ['@id' => $migrationId]));
break;
case MigrationInterface::STATUS_DISABLED:
$this->logger()->warning(dt('Migration @id is disabled', ['@id' => $migrationId]));
break;
case MigrationInterface::STATUS_STOPPING:
$this->logger()->warning(dt('Migration @id is already stopping', ['@id' => $migrationId]));
break;
default:
$migration->interruptMigration(MigrationInterface::RESULT_STOPPED);
$this->logger()->success(dt('Migration @id requested to stop', ['@id' => $migrationId]));
break;
}
}
/**
* Reset an active migration's status to idle.
*
* @command migrate:reset-status
*
* @param string $migrationId
* The ID of migration to reset.
*
* @aliases mrs,migrate-reset-status
*
* @topics docs:migrate
*
* @validate-module-enabled migrate
* @validate-migration-id
* @version 10.4
*
* @throws PluginException
*/
public function resetStatus(string $migrationId): void
{
/** @var MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance($migrationId);
$status = $migration->getStatus();
if ($status == MigrationInterface::STATUS_IDLE) {
$this->logger()->warning(dt('Migration @id is already Idle', ['@id' => $migrationId]));
} else {
$migration->setStatus(MigrationInterface::STATUS_IDLE);
$this->logger()->success(dt('Migration @id reset to Idle', ['@id' => $migrationId]));
}
}
/**
* View any messages associated with a migration.
*
* @command migrate:messages
*
* @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.
*
* @usage migrate:messages article
* Show all messages for the <info>article</info> 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].
* @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"].
*
* @aliases mmsg,migrate-messages
*
* @topics docs:migrate
*
* @validate-module-enabled migrate
* @validate-migration-id
* @version 10.4
*
* @field-labels
* level: Level
* source_ids: Source ID(s)
* destination_ids: Destination ID(s)
* message: Message
* hash: Source IDs hash
* @default-fields level,source_ids,destination_ids,message,hash
*
* @return RowsOfFields
* Migration messages status formatted as table.
*
* @throws PluginException
*/
public function messages(string $migrationId, array $options = ['idlist' => self::REQ]): RowsOfFields
{
/** @var MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance($migrationId);
$idMap = $migration->getIdMap();
$sourceIdKeys = $this->getSourceIdKeys($idMap);
$table = [];
if (empty($sourceIdKeys)) {
// Cannot find one item to extract keys from, no need to process
// messages on an empty ID map.
return new RowsOfFields($table);
}
if (!empty($options['idlist'])) {
// 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);
}
}
return new RowsOfFields($table);
}
foreach ($idMap->getMessages() as $row) {
$table[] = $this->preprocessMessageRow($row, $sourceIdKeys);
}
return new RowsOfFields($table);
}
/**
* Preprocesses migrate message rows.
*
* Given an item inside the list generated by
* MigrateIdMapInterface::getMessages(), prepare it for display.
*
* @param \StdClass $row
* A message to process.
* @param array $sourceIdKeys
* The source IDs keys, for reference.
*
* @see \Drupal\migrate\Plugin\MigrateIdMapInterface::getMessages()
*/
protected function preprocessMessageRow(\StdClass $row, array $sourceIdKeys): array
{
unset($row->msgid);
$row = (array) $row;
// If the message includes useful IDs don't print the hash.
if (count($sourceIdKeys) === count(array_intersect_key($sourceIdKeys, $row))) {
unset($row['source_ids_hash']);
}
$sourceIds = $destinationIds = [];
foreach ($row as $key => $value) {
if (str_starts_with($key, 'src_')) {
$sourceIds[$key] = $value;
}
if (str_starts_with($key, 'dest_')) {
$destinationIds[$key] = $value;
}
}
$row['source_ids'] = implode(' : ', $sourceIds);
$row['destination_ids'] = implode(' : ', $destinationIds);
return $row;
}
/**
* List the fields available for mapping in a source.
*
* @command migrate:fields-source
*
* @param string $migrationId
* The ID of the migration.
*
* @usage migrate:fields-source article
* List fields for the source in the article migration
*
* @aliases mfs,migrate-fields-source
*
* @topics docs:migrate
*
* @validate-module-enabled migrate
* @validate-migration-id
*
* @field-labels
* machine_name: Field name
* description: Description
* @default-fields machine_name,description
* @version 10.4
*
* @return RowsOfFields
* Source fields of the given migration.
*
* @throws PluginException
*/
public function fieldsSource(string $migrationId): RowsOfFields
{
/** @var MigrationInterface $migration */
$migration = $this->migrationPluginManager->createInstance($migrationId);
$source = $migration->getSourcePlugin();
$table = [];
foreach ($source->fields() as $machineName => $description) {
$table[] = [
'machine_name' => $machineName,
'description' => strip_tags($description),
];
}
return new RowsOfFields($table);
}
/**
* Retrieves a list of active migrations.
*
* @param string|null $migrationIds A comma-separated list of migration IDs. If omitted, will return all
* migrations.
* @param string|null $tags
* A comma separated list of tags.
*
* @return MigrationInterface[][]
* An array keyed by migration tag, each value containing an array of
* migrations or an empty array if no migrations match the input criteria.
*
* @throws PluginException
*/
protected function getMigrationList(?string $migrationIds, ?string $tags): array
{
$migrationIds = StringUtils::csvToArray((string) $migrationIds);
$migrations = $this->migrationPluginManager->createInstances($migrationIds);
// Check for invalid migration IDs.
if ($invalidMigrations = array_diff_key(array_flip($migrationIds), $migrations)) {
throw new \InvalidArgumentException('Invalid migration IDs: ' . implode(', ', array_flip($invalidMigrations)));
}
foreach ($migrations as $migrationId => $migration) {
try {
$sourcePlugin = $migration->getSourcePlugin();
if ($sourcePlugin instanceof RequirementsInterface) {
$sourcePlugin->checkRequirements();
}
} catch (RequirementsException $exception) {
$this->logger()->debug("Migration '$migrationId' is skipped as its source plugin has missed requirements: {$exception->getRequirementsString()}");
unset($migrations[$migrationId]);
}
}
// If --tag was not passed, don't group on tags, use a single empty tag.
if ($tags === null) {
return [null => $migrations];
}
$tags = array_filter(array_map('trim', explode(',', $tags)));
$list = [];
foreach ($migrations as $migrationId => $migration) {
$migrationTags = $migration->getMigrationTags();
$commonTags = array_intersect($tags, $migrationTags);
if (!$commonTags) {
// Skip if migration is not tagged with any of the passed tags.
continue;
}
foreach ($commonTags as $tag) {
$list[$tag][$migrationId] = $migration;
}
}
ksort($list);
return $list;
}
/**
* Returns the migrate message logger.
*
* @return MigrateMessageInterface
* The migrate message logger.
*/
protected function getMigrateMessage(): MigrateMessageInterface
{
if (!isset($this->migrateMessage)) {
$this->migrateMessage = new MigrateMessage($this->logger());
}
return $this->migrateMessage;
}
/**
* Get the source ID keys.
*
* @param MigrateIdMapInterface $idMap
* The migration ID map.
*
* @return string[]
* The source ID keys.
*/
protected function getSourceIdKeys(MigrateIdMapInterface $idMap): array
{
if (iterator_count($idMap) === 0) {
// E.g. when the migration has not yet been executed. the ID map is
// empty. do not try to process it.
return [];
}
$idMap->rewind();
$columns = $idMap->currentSource();
$sourceIdKeys = array_map(static function ($id) {
return "src_$id";
}, array_keys($columns));
return array_combine($sourceIdKeys, $sourceIdKeys);
}
/**
* Validates a migration ID is valid.
*
* If the argument to be validated is not named migrationId, pass the
* argument name as the value of the annotation.
*
* @hook validate @validate-migration-id
*
* @param CommandData $commandData
*
* @return CommandError|null
*/
public function validateMigrationId(CommandData $commandData): ?CommandError
{
$argName = $commandData->annotationData()->get('validate-migration-id') ?: 'migrationId';
$migrationId = $commandData->input()->getArgument($argName);
if (!$this->migrationPluginManager->hasDefinition($migrationId)) {
return new CommandError(dt('Migration "@id" does not exist', ['@id' => $migrationId]));
}
return null;
}
}