1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2026-03-21 23:44:03 +00:00
This commit is contained in:
Jimish Gamit 2026-03-09 01:52:25 +00:00 committed by GitHub
commit 192a0e1647
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 140 additions and 4 deletions

View file

@ -17,7 +17,8 @@ class Run extends Command
protected $description = 'Run a command for tenant(s)';
protected $signature = 'tenants:run {commandname : The artisan command.}
{--tenants=* : The tenant(s) to run the command for. Default: all}';
{--tenants=* : The tenant(s) to run the command for. Default: all}
{--skip-tenants=* : The tenant(s) to skip}';
public function handle(): int
{

View file

@ -10,15 +10,16 @@ use Stancl\Tenancy\Database\Concerns\PendingScope;
use Symfony\Component\Console\Input\InputOption;
/**
* Adds 'tenants' and 'with-pending' options.
* Adds 'tenants', 'skip-tenants', and 'with-pending' options.
*/
trait HasTenantOptions
{
protected function getOptions()
{
return array_merge([
new InputOption('tenants', null, InputOption::VALUE_IS_ARRAY|InputOption::VALUE_OPTIONAL, 'The tenants to run this command for. Leave empty for all tenants', null),
new InputOption('with-pending', null, InputOption::VALUE_NONE, 'Include pending tenants in query'), // todo@pending should we also offer without-pending? if we add this, mention in docs
new InputOption('tenants', null, InputOption::VALUE_IS_ARRAY|InputOption::VALUE_OPTIONAL, 'The tenants to run this command for. Leave empty for all tenants', null),
new InputOption('skip-tenants', null, InputOption::VALUE_IS_ARRAY|InputOption::VALUE_OPTIONAL, 'The tenants to skip when running this command', null),
new InputOption('with-pending', null, InputOption::VALUE_NONE, 'Include pending tenants in query'), // todo@pending should we also offer without-pending? if we add this, mention in docs
], parent::getOptions());
}
@ -42,6 +43,9 @@ trait HasTenantOptions
->when($this->option('tenants'), function ($query) {
$query->whereIn(tenancy()->model()->getTenantKeyName(), $this->option('tenants'));
})
->when($this->option('skip-tenants'), function ($query) {
$query->whereNotIn(tenancy()->model()->getTenantKeyName(), $this->option('skip-tenants'));
})
->when(tenancy()->model()::hasGlobalScope(PendingScope::class), function ($query) {
$query->withPending(config('tenancy.pending.include_in_queries') ?: $this->option('with-pending'));
});

View file

@ -515,3 +515,134 @@ test('migrate fresh command only deletes tenant databases if drop_tenant_databas
expect($tenantHasDatabase($tenant))->toBe($shouldHaveDBAfterMigrateFresh);
}
})->with([true, false]);
test('migrate command skips specified tenants', function () {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
$tenant3 = Tenant::create();
Artisan::call('tenants:migrate', [
'--skip-tenants' => [$tenant2->getTenantKey()],
]);
tenancy()->initialize($tenant1);
expect(Schema::hasTable('users'))->toBeTrue();
tenancy()->end();
tenancy()->initialize($tenant2);
expect(Schema::hasTable('users'))->toBeFalse();
tenancy()->end();
tenancy()->initialize($tenant3);
expect(Schema::hasTable('users'))->toBeTrue();
tenancy()->end();
});
test('migrate command skips multiple tenants', function () {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
$tenant3 = Tenant::create();
Artisan::call('tenants:migrate', [
'--skip-tenants' => [$tenant1->getTenantKey(), $tenant2->getTenantKey()],
]);
tenancy()->initialize($tenant1);
expect(Schema::hasTable('users'))->toBeFalse();
tenancy()->end();
tenancy()->initialize($tenant2);
expect(Schema::hasTable('users'))->toBeFalse();
tenancy()->end();
tenancy()->initialize($tenant3);
expect(Schema::hasTable('users'))->toBeTrue();
tenancy()->end();
});
test('run command skips specified tenants', function () {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
$tenant3 = Tenant::create();
Artisan::call('tenants:migrate-fresh');
$id1 = $tenant1->getTenantKey();
$id2 = $tenant2->getTenantKey();
$id3 = $tenant3->getTenantKey();
pest()->artisan("tenants:run --skip-tenants=$id2 'foo foo --b=bar --c=xyz'")
->expectsOutputToContain("Tenant: $id1")
->doesntExpectOutputToContain("Tenant: $id2")
->expectsOutputToContain("Tenant: $id3")
->assertExitCode(0);
});
test('run command skips multiple tenants', function () {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
$tenant3 = Tenant::create();
Artisan::call('tenants:migrate-fresh');
$id1 = $tenant1->getTenantKey();
$id2 = $tenant2->getTenantKey();
$id3 = $tenant3->getTenantKey();
pest()->artisan("tenants:run --skip-tenants=$id1 --skip-tenants=$id2 'foo foo --b=bar --c=xyz'")
->doesntExpectOutputToContain("Tenant: $id1")
->doesntExpectOutputToContain("Tenant: $id2")
->expectsOutputToContain("Tenant: $id3")
->assertExitCode(0);
});
test('tenants and skip-tenants options can be used together', function () {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
$tenant3 = Tenant::create();
Artisan::call('tenants:migrate-fresh');
$id1 = $tenant1->getTenantKey();
$id2 = $tenant2->getTenantKey();
$id3 = $tenant3->getTenantKey();
// Scope to tenant1+tenant2, then skip tenant2 — only tenant1 should run
pest()->artisan("tenants:run --tenants=$id1 --tenants=$id2 --skip-tenants=$id2 'foo foo --b=bar --c=xyz'")
->expectsOutputToContain("Tenant: $id1")
->doesntExpectOutputToContain("Tenant: $id2")
->doesntExpectOutputToContain("Tenant: $id3")
->assertExitCode(0);
});
test('migrate-fresh command skips specified tenants', function () {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
// Migrate all tenants first so both have the users table
Artisan::call('tenants:migrate');
tenancy()->initialize($tenant1);
expect(Schema::hasTable('users'))->toBeTrue();
tenancy()->end();
tenancy()->initialize($tenant2);
expect(Schema::hasTable('users'))->toBeTrue();
tenancy()->end();
// migrate-fresh on tenant1 only (skip tenant2)
pest()->artisan('tenants:migrate-fresh', [
'--skip-tenants' => [$tenant2->getTenantKey()],
'--force' => true,
])->assertExitCode(0);
// tenant1 should still have the table (re-created by migrate-fresh)
tenancy()->initialize($tenant1);
expect(Schema::hasTable('users'))->toBeTrue();
tenancy()->end();
// tenant2 was skipped, so its DB is untouched — table still exists
tenancy()->initialize($tenant2);
expect(Schema::hasTable('users'))->toBeTrue();
tenancy()->end();
});