Skip to content

Commit 03e3a80

Browse files
authored
Fix migrations $connection property (#41161)
Despite being documented as allowing `Schema` queries to run on the given secondary connection, `Schema::connection()` must also be explicitly called to avoid `up()` and `down()` queries to be run on the default database connection. Instead this property is only used by the migrator when wrapping `DB::transaction()` & `DB::pretend()`. The queries still run on the default database connection (or the command option --database.) This change makes queries run using that $connection while not affecting the migrations repository. i.e., the connection for the `migrations` history table. If another `Schema::connection()` is called during `up()` or `down()`, that will be used instead of `$connection`. $connection also overrides MigrateCommand option '--database'. A breaking change is required to switch to the opposite behavior.
1 parent 424bdd4 commit 03e3a80

File tree

4 files changed

+157
-4
lines changed

4 files changed

+157
-4
lines changed

src/Illuminate/Database/Migrations/Migrator.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,11 +387,11 @@ protected function runMigration($migration, $method)
387387
$migration->getConnection()
388388
);
389389

390-
$callback = function () use ($migration, $method) {
390+
$callback = function () use ($connection, $migration, $method) {
391391
if (method_exists($migration, $method)) {
392392
$this->fireMigrationEvent(new MigrationStarted($migration, $method));
393393

394-
$migration->{$method}();
394+
$this->runMethod($connection, $migration, $method);
395395

396396
$this->fireMigrationEvent(new MigrationEnded($migration, $method));
397397
}
@@ -447,13 +447,34 @@ protected function getQueries($migration, $method)
447447
$migration->getConnection()
448448
);
449449

450-
return $db->pretend(function () use ($migration, $method) {
450+
return $db->pretend(function () use ($db, $migration, $method) {
451451
if (method_exists($migration, $method)) {
452-
$migration->{$method}();
452+
$this->runMethod($db, $migration, $method);
453453
}
454454
});
455455
}
456456

457+
/**
458+
* Run a migration method on the given connection.
459+
*
460+
* @param \Illuminate\Database\Connection $connection
461+
* @param object $migration
462+
* @param string $method
463+
* @return void
464+
*/
465+
protected function runMethod($connection, $migration, $method)
466+
{
467+
$previousConnection = $this->resolver->getDefaultConnection();
468+
469+
try {
470+
$this->resolver->setDefaultConnection($connection->getName());
471+
472+
$migration->{$method}();
473+
} finally {
474+
$this->resolver->setDefaultConnection($previousConnection);
475+
}
476+
}
477+
457478
/**
458479
* Resolve a migration instance from a file.
459480
*

tests/Database/DatabaseMigratorIntegrationTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ protected function setUp(): void
3737
'database' => ':memory:',
3838
], 'sqlite2');
3939

40+
$db->addConnection([
41+
'driver' => 'sqlite',
42+
'database' => ':memory:',
43+
], 'sqlite3');
44+
4045
$db->setAsGlobal();
4146

4247
$container = new Container;
@@ -84,6 +89,55 @@ public function testBasicMigrationOfSingleFolder()
8489
$this->assertTrue(Str::contains($ran[1], 'password_resets'));
8590
}
8691

92+
public function testMigrationsDefaultConnectionCanBeChanged()
93+
{
94+
$ran = $this->migrator->usingConnection('sqlite2', function () {
95+
return $this->migrator->run([__DIR__.'/migrations/one'], ['database' => 'sqllite3']);
96+
});
97+
98+
$this->assertFalse($this->db->schema()->hasTable('users'));
99+
$this->assertFalse($this->db->schema()->hasTable('password_resets'));
100+
$this->assertTrue($this->db->schema('sqlite2')->hasTable('users'));
101+
$this->assertTrue($this->db->schema('sqlite2')->hasTable('password_resets'));
102+
$this->assertFalse($this->db->schema('sqlite3')->hasTable('users'));
103+
$this->assertFalse($this->db->schema('sqlite3')->hasTable('password_resets'));
104+
105+
$this->assertTrue(Str::contains($ran[0], 'users'));
106+
$this->assertTrue(Str::contains($ran[1], 'password_resets'));
107+
}
108+
109+
public function testMigrationsCanEachDefineConnection()
110+
{
111+
$ran = $this->migrator->run([__DIR__.'/migrations/connection_configured']);
112+
113+
$this->assertFalse($this->db->schema()->hasTable('failed_jobs'));
114+
$this->assertFalse($this->db->schema()->hasTable('jobs'));
115+
$this->assertFalse($this->db->schema('sqlite2')->hasTable('failed_jobs'));
116+
$this->assertFalse($this->db->schema('sqlite2')->hasTable('jobs'));
117+
$this->assertTrue($this->db->schema('sqlite3')->hasTable('failed_jobs'));
118+
$this->assertTrue($this->db->schema('sqlite3')->hasTable('jobs'));
119+
120+
$this->assertTrue(Str::contains($ran[0], 'failed_jobs'));
121+
$this->assertTrue(Str::contains($ran[1], 'jobs'));
122+
}
123+
124+
public function testMigratorCannotChangeDefinedMigrationConnection()
125+
{
126+
$ran = $this->migrator->usingConnection('sqlite2', function () {
127+
return $this->migrator->run([__DIR__.'/migrations/connection_configured']);
128+
});
129+
130+
$this->assertFalse($this->db->schema()->hasTable('failed_jobs'));
131+
$this->assertFalse($this->db->schema()->hasTable('jobs'));
132+
$this->assertFalse($this->db->schema('sqlite2')->hasTable('failed_jobs'));
133+
$this->assertFalse($this->db->schema('sqlite2')->hasTable('jobs'));
134+
$this->assertTrue($this->db->schema('sqlite3')->hasTable('failed_jobs'));
135+
$this->assertTrue($this->db->schema('sqlite3')->hasTable('jobs'));
136+
137+
$this->assertTrue(Str::contains($ran[0], 'failed_jobs'));
138+
$this->assertTrue(Str::contains($ran[1], 'jobs'));
139+
}
140+
87141
public function testMigrationsCanBeRolledBack()
88142
{
89143
$this->migrator->run([__DIR__.'/migrations/one']);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
/**
10+
* The database connection that should be used by the migration.
11+
*
12+
* @var string
13+
*/
14+
protected $connection = 'sqlite3';
15+
16+
/**
17+
* Run the migrations.
18+
*
19+
* @return void
20+
*/
21+
public function up()
22+
{
23+
Schema::create('failed_jobs', function (Blueprint $table) {
24+
$table->id();
25+
$table->text('connection');
26+
$table->text('queue');
27+
$table->longText('payload');
28+
$table->longText('exception');
29+
$table->timestamp('failed_at')->useCurrent();
30+
});
31+
}
32+
33+
/**
34+
* Reverse the migrations.
35+
*
36+
* @return void
37+
*/
38+
public function down()
39+
{
40+
Schema::dropIfExists('failed_jobs');
41+
}
42+
};
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+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::connection('sqlite3')->create('jobs', function (Blueprint $table) {
17+
$table->bigIncrements('id');
18+
$table->string('queue')->index();
19+
$table->longText('payload');
20+
$table->unsignedTinyInteger('attempts');
21+
$table->unsignedInteger('reserved_at')->nullable();
22+
$table->unsignedInteger('available_at');
23+
$table->unsignedInteger('created_at');
24+
});
25+
}
26+
27+
/**
28+
* Reverse the migrations.
29+
*
30+
* @return void
31+
*/
32+
public function down()
33+
{
34+
Schema::connection('sqlite3')->dropIfExists('jobs');
35+
}
36+
};

0 commit comments

Comments
 (0)