Skip to content

Commit 6e64e7d

Browse files
authored
4.next migrate/rollback count (#874)
* Add count to migrate/rollback * Add guard check. * Add tests
1 parent e13d291 commit 6e64e7d

File tree

6 files changed

+312
-11
lines changed

6 files changed

+312
-11
lines changed

src/CakeManager.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function printStatus(string $environment, ?string $format = null): array
7171
$migrations = [];
7272
$isJson = $format === 'json';
7373
$defaultMigrations = $this->getMigrations('default');
74-
if (count($defaultMigrations)) {
74+
if ($defaultMigrations) {
7575
$env = $this->getEnvironment($environment);
7676
$versions = $env->getVersionLog();
7777
$this->maxNameLength = $versions ? max(array_map(function ($version) {

src/Command/MigrateCommand.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Cake\Event\EventDispatcherTrait;
2121
use DateTime;
2222
use Exception;
23+
use LogicException;
2324
use Migrations\Config\ConfigInterface;
2425
use Migrations\Migration\ManagerFactory;
2526
use Throwable;
@@ -126,9 +127,15 @@ protected function executeMigrations(Arguments $args, ConsoleIo $io): ?int
126127
$date = $args->getOption('date');
127128
$fake = (bool)$args->getOption('fake');
128129

129-
$count = $args->getOption('count');
130-
if ($count) {
131-
$io->abort('The `--count` option is not supported yet in this command. Use `--target` instead.');
130+
$count = $args->getOption('count') !== null ? (int)$args->getOption('count') : null;
131+
if ($count !== null && $count < 1) {
132+
throw new LogicException('Count must be > 0.');
133+
}
134+
if ($count && $date) {
135+
throw new LogicException('Can only use one of `--count` or `--date` options at a time.');
136+
}
137+
if ($version && $date) {
138+
throw new LogicException('Can only use one of `--version` or `--date` options at a time.');
132139
}
133140

134141
$factory = new ManagerFactory([
@@ -160,7 +167,7 @@ protected function executeMigrations(Arguments $args, ConsoleIo $io): ?int
160167
if ($date !== null) {
161168
$manager->migrateToDateTime(new DateTime((string)$date), $fake);
162169
} else {
163-
$manager->migrate($version, $fake);
170+
$manager->migrate($version, $fake, $count);
164171
}
165172
$end = microtime(true);
166173
} catch (Exception $e) {

src/Command/RollbackCommand.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use DateTime;
2222
use Exception;
2323
use InvalidArgumentException;
24+
use LogicException;
2425
use Migrations\Config\ConfigInterface;
2526
use Migrations\Migration\ManagerFactory;
2627
use Throwable;
@@ -77,6 +78,9 @@ public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionPar
7778
])->addOption('date', [
7879
'short' => 'd',
7980
'help' => 'The date to rollback to',
81+
])->addOption('count', [
82+
'short' => 'k',
83+
'help' => 'The number of migrations to rollback',
8084
])->addOption('fake', [
8185
'help' => "Mark any migrations selected as run, but don't actually execute them",
8286
'boolean' => true,
@@ -130,6 +134,17 @@ protected function executeMigrations(Arguments $args, ConsoleIo $io): ?int
130134
$force = (bool)$args->getOption('force');
131135
$dryRun = (bool)$args->getOption('dry-run');
132136

137+
$count = $args->getOption('count') !== null ? (int)$args->getOption('count') : null;
138+
if ($count !== null && $count < 1) {
139+
throw new LogicException('Count must be > 0.');
140+
}
141+
if ($count && $date) {
142+
throw new LogicException('Can only use one of `--count` or `--date` options at a time.');
143+
}
144+
if ($version && $date) {
145+
throw new LogicException('Can only use one of `--version` or `--date` options at a time.');
146+
}
147+
133148
$factory = new ManagerFactory([
134149
'plugin' => $args->getOption('plugin'),
135150
'source' => $args->getOption('source'),
@@ -162,7 +177,11 @@ protected function executeMigrations(Arguments $args, ConsoleIo $io): ?int
162177
try {
163178
// run the migrations
164179
$start = microtime(true);
165-
$manager->rollback($target, $force, $targetMustMatch, $fake);
180+
if ($count) {
181+
$manager->rollbackByCount($count, $force, $fake);
182+
} else {
183+
$manager->rollback($target, $force, $targetMustMatch, $fake);
184+
}
166185
$end = microtime(true);
167186
} catch (Exception $e) {
168187
$io->err('<error>' . $e->getMessage() . '</error>');

src/Migration/Manager.php

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public function printStatus(?string $format = null): array
8282
$migrations = [];
8383
$isJson = $format === 'json';
8484
$defaultMigrations = $this->getMigrations();
85-
if (count($defaultMigrations)) {
85+
if ($defaultMigrations) {
8686
$env = $this->getEnvironment();
8787
$versions = $env->getVersionLog();
8888

@@ -361,13 +361,14 @@ public function markVersionsAsMigrated(string $path, array $versions): array
361361
}
362362

363363
/**
364-
* Migrate an environment to the specified version.
364+
* Migrate an environment to the specified version or by count of migrations.
365365
*
366366
* @param int|null $version version to migrate to
367367
* @param bool $fake flag that if true, we just record running the migration, but not actually do the migration
368+
* @param int|null $count Number of migrations to run, all migrations will be run if not set and no version is given.
368369
* @return void
369370
*/
370-
public function migrate(?int $version = null, bool $fake = false): void
371+
public function migrate(?int $version = null, bool $fake = false, ?int $count = null): void
371372
{
372373
$migrations = $this->getMigrations();
373374
$env = $this->getEnvironment();
@@ -409,13 +410,15 @@ public function migrate(?int $version = null, bool $fake = false): void
409410
}
410411

411412
ksort($migrations);
413+
$done = 0;
412414
foreach ($migrations as $migration) {
413-
if ($migration->getVersion() > $version) {
415+
if ($migration->getVersion() > $version || ($count && $done >= $count)) {
414416
break;
415417
}
416418

417419
if (!in_array($migration->getVersion(), $versions)) {
418420
$this->executeMigration($migration, MigrationInterface::UP, $fake);
421+
$done++;
419422
}
420423
}
421424
}
@@ -535,6 +538,38 @@ protected function printStatusOutput(string $name, string $status, ?string $dura
535538
);
536539
}
537540

541+
/**
542+
* Rollback an environment by a specific count of migrations.
543+
*
544+
* Note: If the count is greater than the number of migrations, it will rollback all migrations.
545+
*
546+
* @param int $count Count
547+
* @param bool $force Force
548+
* @param bool $fake Flag that if true, we just record running the migration, but not actually do the migration
549+
* @return void
550+
*/
551+
public function rollbackByCount(int $count, bool $force = false, bool $fake = false): void
552+
{
553+
// note that the version log are also indexed by name with the proper ascending order according to the version order
554+
$executedVersions = $this->getEnvironment()->getVersionLog();
555+
556+
$total = count($executedVersions);
557+
$pos = 0;
558+
while ($pos < $count && $pos < $total) {
559+
array_pop($executedVersions);
560+
$pos++;
561+
}
562+
563+
if ($executedVersions) {
564+
$last = end($executedVersions);
565+
$target = $last['version'];
566+
} else {
567+
$target = 0;
568+
}
569+
570+
$this->rollback($target, $force, false, $fake);
571+
}
572+
538573
/**
539574
* Rollback an environment to the specified version.
540575
*

tests/TestCase/Command/CompletionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public function testMigrationsOptionsRollback()
103103
$this->exec('completion options migrations.migrations rollback');
104104
$this->assertCount(1, $this->_out->messages());
105105
$output = $this->_out->messages()[0];
106-
$expected = '--connection -c --date -d --dry-run -x --fake --force -f --help -h --no-lock --plugin -p';
106+
$expected = '--connection -c --count -k --date -d --dry-run -x --fake --force -f --help -h --no-lock --plugin -p';
107107
$expected .= ' --quiet -q --source -s --target -t --verbose -v';
108108
$outputExplode = explode(' ', trim($output));
109109
sort($outputExplode);

0 commit comments

Comments
 (0)