From 3b42c9e20ca8a502d20e627e2ecb8978732a6076 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Mon, 25 Aug 2025 15:57:15 +0200 Subject: [PATCH] [4.x] Use --database in tenants:migrate as the template connection (#1386) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make the `--database` option passed to `tenants:migrate` use the passed connection as the tenant connection template * Reset template connection regardless of process count --------- Co-authored-by: Samuel Ć tancl --- src/Commands/Migrate.php | 21 +++++++++++---- tests/CommandsTest.php | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/Commands/Migrate.php b/src/Commands/Migrate.php index 5348b509..6ecd6e14 100644 --- a/src/Commands/Migrate.php +++ b/src/Commands/Migrate.php @@ -51,13 +51,24 @@ class Migrate extends MigrateCommand return 1; } - if ($this->getProcesses() > 1) { - return $this->runConcurrently($this->getTenantChunks()->map(function ($chunk) { - return $this->getTenants($chunk); - })); + $originalTemplateConnection = config('tenancy.database.template_tenant_connection'); + + if ($database = $this->input->getOption('database')) { + config(['tenancy.database.template_tenant_connection' => $database]); } - return $this->migrateTenants($this->getTenants()) ? 0 : 1; + if ($this->getProcesses() > 1) { + $code = $this->runConcurrently($this->getTenantChunks()->map(function ($chunk) { + return $this->getTenants($chunk); + })); + } else { + $code = $this->migrateTenants($this->getTenants()) ? 0 : 1; + } + + // Reset the template tenant connection to the original one + config(['tenancy.database.template_tenant_connection' => $originalTemplateConnection]); + + return $code; } protected function childHandle(mixed ...$args): bool diff --git a/tests/CommandsTest.php b/tests/CommandsTest.php index 16aecd92..a5b3b856 100644 --- a/tests/CommandsTest.php +++ b/tests/CommandsTest.php @@ -27,6 +27,7 @@ use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper; use Stancl\Tenancy\Bootstrappers\FilesystemTenancyBootstrapper; use Stancl\Tenancy\Database\Exceptions\TenantDatabaseDoesNotExistException; use function Stancl\Tenancy\Tests\pest; +use Stancl\Tenancy\Events\MigratingDatabase; beforeEach(function () { if (file_exists($schemaPath = 'tests/Etc/tenant-schema-test.dump')) { @@ -95,6 +96,60 @@ test('migrate command works with tenants option', function () { expect(Schema::hasTable('users'))->toBeTrue(); }); +test('migrate command uses the passed database option as the template tenant connection', function () { + $originalTemplateConnection = config('tenancy.database.template_tenant_connection'); + + // Add a custom connection that will be used as the template for the tenant connection + // Identical to the default (mysql), just with different charset and collation + config(['database.connections.custom_connection' => [ + "driver" => "mysql", + "url" => "", + "host" => "mysql", + "port" => "3306", + "database" => "main", + "username" => "root", + "password" => "password", + "unix_socket" => "", + "charset" => "latin1", // Different from the default (utf8mb4) + "collation" => "latin1_swedish_ci", // Different from the default (utf8mb4_unicode_ci) + "prefix" => "", + "prefix_indexes" => true, + "strict" => true, + "engine" => null, + "options" => [] + ]]); + + $templateConnectionDuringMigration = null; + $tenantConnectionDuringMigration = null; + + Event::listen(MigratingDatabase::class, function() use (&$templateConnectionDuringMigration, &$tenantConnectionDuringMigration) { + $templateConnectionDuringMigration = config('tenancy.database.template_tenant_connection'); + $tenantConnectionDuringMigration = DB::connection('tenant')->getConfig(); + }); + + // The original tenant template connection config remains default + expect(config('tenancy.database.template_tenant_connection'))->toBe($originalTemplateConnection); + + Tenant::create(); + + // The original template connection is used when the --database option is not passed + pest()->artisan('tenants:migrate'); + expect($templateConnectionDuringMigration)->toBe($originalTemplateConnection); + + Tenant::create(); + + // The migrate command temporarily uses the connection passed in the --database option + pest()->artisan('tenants:migrate', ['--database' => 'custom_connection']); + expect($templateConnectionDuringMigration)->toBe('custom_connection'); + + // The tenant connection during migration actually used custom_connection's config + expect($tenantConnectionDuringMigration['charset'])->toBe('latin1'); + expect($tenantConnectionDuringMigration['collation'])->toBe('latin1_swedish_ci'); + + // The tenant template connection config is restored to the original after migrating + expect(config('tenancy.database.template_tenant_connection'))->toBe($originalTemplateConnection); +}); + test('migrate command only throws exceptions if skip-failing is not passed', function() { Tenant::create();