diff --git a/lib/Doctrine/Migrations/Tools/Console/Command/ExecuteCommand.php b/lib/Doctrine/Migrations/Tools/Console/Command/ExecuteCommand.php
index 77f5d9dcc..0180001a0 100644
--- a/lib/Doctrine/Migrations/Tools/Console/Command/ExecuteCommand.php
+++ b/lib/Doctrine/Migrations/Tools/Console/Command/ExecuteCommand.php
@@ -38,7 +38,10 @@ protected function configure(): void
->addArgument(
'versions',
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
- 'The versions to execute.',
+ sprintf(
+ 'The versions to execute. Use the FQCN of the migration, or the following reserved aliases: %s',
+ implode(', ', ['first', 'current', 'prev', 'next', 'latest'])
+ ),
null
)
->addOption(
@@ -76,6 +79,10 @@ protected function configure(): void
The %command.name% command executes migration versions up or down manually:
%command.full_name% FQCN
+
+You can use version aliases instead of the FQCN:
+
+ %command.full_name% current
You can show more information about the process by increasing the verbosity level. To see the
executed queries, set the level to debug with -vv:
@@ -113,20 +120,31 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$migratorConfigurationFactory = $this->getDependencyFactory()->getConsoleInputMigratorConfigurationFactory();
$migratorConfiguration = $migratorConfigurationFactory->getMigratorConfiguration($input);
+ $aliasResolver = $this->getDependencyFactory()->getVersionAliasResolver();
- $question = sprintf(
- 'WARNING! You are about to execute a migration in database "%s" that could result in schema changes and data loss. Are you sure you wish to continue?',
- $this->getDependencyFactory()->getConnection()->getDatabase() ?? ''
- );
- if (! $migratorConfiguration->isDryRun() && ! $this->canExecute($question, $input)) {
- $this->io->error('Migration cancelled!');
+ $versions = $input->getArgument('versions');
- return 1;
+ foreach ($versions as &$version) {
+ $version = (string) $aliasResolver->resolveVersionAlias($version);
+ }
+
+ if (! $migratorConfiguration->isDryRun()) {
+ $this->io->listing($versions);
+
+ $question = sprintf(
+ 'WARNING! You are about to execute the migrations listed above in database "%s" that could result in schema changes and data loss. Are you sure you wish to continue?',
+ $this->getDependencyFactory()->getConnection()->getDatabase() ?? ''
+ );
+
+ if (! $this->canExecute($question, $input)) {
+ $this->io->error('Migration cancelled!');
+
+ return 1;
+ }
}
$this->getDependencyFactory()->getMetadataStorage()->ensureInitialized();
- $versions = $input->getArgument('versions');
$direction = $input->getOption('down') !== false
? Direction::DOWN
: Direction::UP;
diff --git a/tests/Doctrine/Migrations/Tests/Tools/Console/Command/ExecuteCommandTest.php b/tests/Doctrine/Migrations/Tests/Tools/Console/Command/ExecuteCommandTest.php
index f299f44fd..a85ed730f 100644
--- a/tests/Doctrine/Migrations/Tests/Tools/Console/Command/ExecuteCommandTest.php
+++ b/tests/Doctrine/Migrations/Tests/Tools/Console/Command/ExecuteCommandTest.php
@@ -9,6 +9,7 @@
use Doctrine\Migrations\Configuration\Connection\ExistingConnection;
use Doctrine\Migrations\Configuration\Migration\ExistingConfiguration;
use Doctrine\Migrations\DependencyFactory;
+use Doctrine\Migrations\Exception\UnknownMigrationVersion;
use Doctrine\Migrations\Metadata\MigrationPlan;
use Doctrine\Migrations\Metadata\MigrationPlanList;
use Doctrine\Migrations\Metadata\Storage\TableMetadataStorageConfiguration;
@@ -17,6 +18,7 @@
use Doctrine\Migrations\QueryWriter;
use Doctrine\Migrations\Tests\MigrationTestCase;
use Doctrine\Migrations\Tools\Console\Command\ExecuteCommand;
+use Doctrine\Migrations\Version\AliasResolver;
use Doctrine\Migrations\Version\Direction;
use Doctrine\Migrations\Version\MigrationPlanCalculator;
use Doctrine\Migrations\Version\Version;
@@ -47,6 +49,9 @@ class ExecuteCommandTest extends MigrationTestCase
/** @var MigrationPlanCalculator|MockObject */
private $planCalculator;
+ /** @var AliasResolver|MockObject */
+ private $versionAliasResolver;
+
/**
* @param bool|string|null $arg
*
@@ -63,6 +68,12 @@ public function testWriteSql(bool $dryRun, $arg, ?string $path): void
return ['A'];
});
+ $this->versionAliasResolver
+ ->expects(self::once())
+ ->method('resolveVersionAlias')
+ ->with('1')
+ ->willReturn(new Version('1'));
+
if ($arg === false) {
$this->queryWriter
->expects(self::never())
@@ -116,6 +127,12 @@ public function testExecute(): void
return ['A'];
});
+ $this->versionAliasResolver
+ ->expects(self::once())
+ ->method('resolveVersionAlias')
+ ->with('1')
+ ->willReturn(new Version('1'));
+
$this->executeCommandTester->execute([
'versions' => ['1'],
'--down' => true,
@@ -125,6 +142,63 @@ public function testExecute(): void
self::assertStringContainsString('[notice] Executing 1 up', trim($this->executeCommandTester->getDisplay(true)));
}
+ public function testExecuteVersionAlias(): void
+ {
+ $this->executeCommandTester->setInputs(['yes']);
+
+ $this->migrator
+ ->expects(self::once())
+ ->method('migrate')
+ ->willReturnCallback(static function (MigrationPlanList $planList, MigratorConfiguration $configuration): array {
+ self::assertFalse($configuration->isDryRun());
+
+ return ['A'];
+ });
+
+ $this->versionAliasResolver
+ ->expects(self::once())
+ ->method('resolveVersionAlias')
+ ->with('current')
+ ->willReturn(new Version('1'));
+
+ $this->executeCommandTester->execute([
+ 'versions' => ['current'],
+ '--down' => true,
+ ]);
+
+ self::assertSame(0, $this->executeCommandTester->getStatusCode());
+ self::assertStringContainsString('[notice] Executing 1 up', trim($this->executeCommandTester->getDisplay(true)));
+ }
+
+ public function testExecuteVersionAliasException(): void
+ {
+ $this->executeCommandTester->setInputs(['yes']);
+
+ $this->migrator
+ ->expects(self::never())
+ ->method('migrate')
+ ->willReturnCallback(static function (MigrationPlanList $planList, MigratorConfiguration $configuration): array {
+ self::assertFalse($configuration->isDryRun());
+
+ return ['A'];
+ });
+
+ $exception = new UnknownMigrationVersion('not_a_valid_version_alias');
+
+ $this->versionAliasResolver
+ ->expects(self::once())
+ ->method('resolveVersionAlias')
+ ->with('not_a_valid_version_alias')
+ ->willThrowException($exception);
+
+ self::expectExceptionObject($exception);
+
+ $this->executeCommandTester->execute([
+ 'versions' => ['not_a_valid_version_alias'],
+ '--down' => true,
+ ]);
+ }
+
public function testExecuteMultiple(): void
{
$migration = $this->createMock(AbstractMigration::class);
@@ -148,6 +222,12 @@ public function testExecuteMultiple(): void
return ['A'];
});
+ $this->versionAliasResolver
+ ->expects(self::exactly(2))
+ ->method('resolveVersionAlias')
+ ->withConsecutive(['1'], ['2'])
+ ->willReturnOnConsecutiveCalls(new Version('1'), new Version('2'));
+
$this->executeCommandTester->execute([
'versions' => ['1', '2'],
'--down' => true,
@@ -174,6 +254,12 @@ public function testExecuteCancel(): void
return ['A'];
});
+ $this->versionAliasResolver
+ ->expects(self::once())
+ ->method('resolveVersionAlias')
+ ->with('1')
+ ->willReturn(new Version('1'));
+
$this->executeCommandTester->execute([
'versions' => ['1'],
'--down' => true,
@@ -186,9 +272,10 @@ protected function setUp(): void
{
$connection = $this->getSqliteConnection();
- $this->migrator = $this->createMock(Migrator::class);
- $this->queryWriter = $this->createMock(QueryWriter::class);
- $migration = $this->createMock(AbstractMigration::class);
+ $this->migrator = $this->createMock(Migrator::class);
+ $this->queryWriter = $this->createMock(QueryWriter::class);
+ $this->versionAliasResolver = $this->createMock(AliasResolver::class);
+ $migration = $this->createMock(AbstractMigration::class);
$p1 = new MigrationPlan(new Version('1'), $migration, Direction::UP);
$pl = new MigrationPlanList([$p1], Direction::UP);
@@ -207,6 +294,7 @@ protected function setUp(): void
$this->dependencyFactory->setService(Migrator::class, $this->migrator);
$this->dependencyFactory->setService(MigrationPlanCalculator::class, $this->planCalculator);
$this->dependencyFactory->setService(QueryWriter::class, $this->queryWriter);
+ $this->dependencyFactory->setService(AliasResolver::class, $this->versionAliasResolver);
$this->executeCommand = new ExecuteCommand($this->dependencyFactory);