Skip to content

Commit b3dc56c

Browse files
Add an optional shouldRun method to migrations. (#55011)
* feat(migrations): shouldRun method on migration classes. * Add test. * styleCI fixes * Formatting change. * StyleCI * formatting * chore: refactor to avoid issues with changing how migrations are loaded - Refactored where the skip check is done - Added in a 'SKIPPED' status message for skipped migrations - Added in a new MigrationResult backed enum to store the new third 'state' of a migration ran. * fix: add back method * fix: removed unused method failing tests. * fix: tests. * StyleCI * fix: remove dd() * formatting --------- Co-authored-by: Taylor Otwell <taylor@laravel.com>
1 parent 27acc09 commit b3dc56c

File tree

8 files changed

+126
-11
lines changed

8 files changed

+126
-11
lines changed

src/Illuminate/Console/View/Components/Task.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Illuminate\Console\View\Components;
44

5+
use Illuminate\Database\Migrations\MigrationResult;
56
use Illuminate\Support\InteractsWithTime;
67
use Symfony\Component\Console\Output\OutputInterface;
78
use Throwable;
@@ -34,10 +35,10 @@ public function render($description, $task = null, $verbosity = OutputInterface:
3435

3536
$startTime = microtime(true);
3637

37-
$result = false;
38+
$result = MigrationResult::Failure;
3839

3940
try {
40-
$result = ($task ?: fn () => true)();
41+
$result = ($task ?: fn () => MigrationResult::Success)();
4142
} catch (Throwable $e) {
4243
throw $e;
4344
} finally {
@@ -53,7 +54,11 @@ public function render($description, $task = null, $verbosity = OutputInterface:
5354
$this->output->write("<fg=gray>$runTime</>", false, $verbosity);
5455

5556
$this->output->writeln(
56-
$result !== false ? ' <fg=green;options=bold>DONE</>' : ' <fg=red;options=bold>FAIL</>',
57+
match ($result) {
58+
MigrationResult::Failure => ' <fg=red;options=bold>FAIL</>',
59+
MigrationResult::Skipped => ' <fg=yellow;options=bold>SKIPPED</>',
60+
default => ' <fg=green;options=bold>DONE</>'
61+
},
5762
$verbosity,
5863
);
5964
}

src/Illuminate/Database/Migrations/Migration.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,14 @@ public function getConnection()
2727
{
2828
return $this->connection;
2929
}
30+
31+
/**
32+
* Determine if this migration should run.
33+
*
34+
* @return bool
35+
*/
36+
public function shouldRun(): bool
37+
{
38+
return true;
39+
}
3040
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Illuminate\Database\Migrations;
4+
5+
enum MigrationResult: int
6+
{
7+
case Success = 1;
8+
case Failure = 2;
9+
case Skipped = 3;
10+
}

src/Illuminate/Database/Migrations/Migrator.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,20 @@ protected function runUp($file, $batch, $pretend)
240240
return $this->pretendToRun($migration, 'up');
241241
}
242242

243-
$this->write(Task::class, $name, fn () => $this->runMigration($migration, 'up'));
243+
$shouldRunMigration = $migration instanceof Migration
244+
? $migration->shouldRun()
245+
: true;
244246

245-
// Once we have run a migrations class, we will log that it was run in this
246-
// repository so that we don't try to run it next time we do a migration
247-
// in the application. A migration repository keeps the migrate order.
248-
$this->repository->log($name, $batch);
247+
if (! $shouldRunMigration) {
248+
$this->write(Task::class, $name, fn () => MigrationResult::Skipped);
249+
} else {
250+
$this->write(Task::class, $name, fn () => $this->runMigration($migration, 'up'));
251+
252+
// Once we have run a migrations class, we will log that it was run in this
253+
// repository so that we don't try to run it next time we do a migration
254+
// in the application. A migration repository keeps the migrate order.
255+
$this->repository->log($name, $batch);
256+
}
249257
}
250258

251259
/**

tests/Console/View/ComponentsTest.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Console\OutputStyle;
66
use Illuminate\Console\View\Components;
7+
use Illuminate\Database\Migrations\MigrationResult;
78
use Mockery as m;
89
use PHPUnit\Framework\TestCase;
910
use Symfony\Component\Console\Output\BufferedOutput;
@@ -108,15 +109,20 @@ public function testTask()
108109
{
109110
$output = new BufferedOutput();
110111

111-
with(new Components\Task($output))->render('My task', fn () => true);
112+
with(new Components\Task($output))->render('My task', fn () => MigrationResult::Success);
112113
$result = $output->fetch();
113114
$this->assertStringContainsString('My task', $result);
114115
$this->assertStringContainsString('DONE', $result);
115116

116-
with(new Components\Task($output))->render('My task', fn () => false);
117+
with(new Components\Task($output))->render('My task', fn () => MigrationResult::Failure);
117118
$result = $output->fetch();
118119
$this->assertStringContainsString('My task', $result);
119120
$this->assertStringContainsString('FAIL', $result);
121+
122+
with(new Components\Task($output))->render('My task', fn () => MigrationResult::Skipped);
123+
$result = $output->fetch();
124+
$this->assertStringContainsString('My task', $result);
125+
$this->assertStringContainsString('SKIPPED', $result);
120126
}
121127

122128
public function testTwoColumnDetail()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateFlightsTable extends Migration
8+
{
9+
public function shouldRun(): bool
10+
{
11+
return false;
12+
}
13+
14+
/**
15+
* Run the migrations.
16+
*
17+
* @return void
18+
*/
19+
public function up()
20+
{
21+
Schema::create('flights', function (Blueprint $table) {
22+
$table->increments('id');
23+
$table->string('name');
24+
});
25+
}
26+
27+
/**
28+
* Reverse the migrations.
29+
*
30+
* @return void
31+
*/
32+
public function down()
33+
{
34+
Schema::dropIfExists('flights');
35+
}
36+
}

tests/Integration/Migration/MigratorTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public function testMigrate()
4444
$this->expectTask('2014_10_12_000000_create_people_table', 'DONE');
4545
$this->expectTask('2015_10_04_000000_modify_people_table', 'DONE');
4646
$this->expectTask('2016_10_04_000000_modify_people_table', 'DONE');
47+
$this->expectTask('2017_10_04_000000_add_age_to_people', 'SKIPPED');
4748

4849
$this->output->shouldReceive('writeln')->once();
4950

@@ -52,6 +53,7 @@ public function testMigrate()
5253
$this->assertTrue(DB::getSchemaBuilder()->hasTable('people'));
5354
$this->assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'first_name'));
5455
$this->assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'last_name'));
56+
$this->assertFalse(DB::getSchemaBuilder()->hasColumn('people', 'age'));
5557
}
5658

5759
public function testMigrateWithoutOutput()
@@ -64,6 +66,7 @@ public function testMigrateWithoutOutput()
6466
$this->assertTrue(DB::getSchemaBuilder()->hasTable('people'));
6567
$this->assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'first_name'));
6668
$this->assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'last_name'));
69+
$this->assertFalse(DB::getSchemaBuilder()->hasColumn('people', 'age'));
6770
}
6871

6972
public function testWithSkippedMigrations()
@@ -120,7 +123,7 @@ public function testPretendMigrate()
120123
$this->expectTwoColumnDetail('2016_10_04_000000_modify_people_table');
121124
$this->expectBulletList(['alter table "people" add column "last_name" varchar']);
122125

123-
$this->output->shouldReceive('writeln')->once();
126+
$this->output->shouldReceive('writeln')->times(3);
124127

125128
$this->subject->run([__DIR__.'/fixtures'], ['pretend' => true]);
126129

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function shouldRun(): bool
10+
{
11+
return false;
12+
}
13+
14+
/**
15+
* Run the migrations.
16+
*
17+
* @return void
18+
*/
19+
public function up()
20+
{
21+
Schema::table('people', function (Blueprint $table) {
22+
$table->unsignedInteger('age')->nullable();
23+
});
24+
}
25+
26+
/**
27+
* Reverse the migrations.
28+
*
29+
* @return void
30+
*/
31+
public function down()
32+
{
33+
Schema::table('people', function (Blueprint $table) {
34+
$table->dropColumn('age');
35+
});
36+
}
37+
};

0 commit comments

Comments
 (0)