Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Commands/Run.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
10 changes: 7 additions & 3 deletions src/Concerns/HasTenantOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
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_OPTIONAL, 'Include pending tenants in query if true/1, exclude if false/0. Defaults to the tenancy.pending.include_in_queries config value.'),
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_OPTIONAL, 'Include pending tenants in query if true/1, exclude if false/0. Defaults to the tenancy.pending.include_in_queries config value.'),
], parent::getOptions());
}

Expand All @@ -42,6 +43,9 @@ protected function getTenantsQuery(?array $tenantKeys = null): Builder
->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) {
$includePending = $this->input->hasParameterOption('--with-pending')
? filter_var($this->option('with-pending') ?? true, FILTER_VALIDATE_BOOLEAN)
Expand Down
48 changes: 48 additions & 0 deletions tests/CommandsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,51 @@
expect($tenantHasDatabase($tenant))->toBe($shouldHaveDBAfterMigrateFresh);
}
})->with([true, false]);

test('migrate commands can skip specified tenants', function (string $command) {
$tenant1 = Tenant::create();
$tenant2 = Tenant::create();
$tenant3 = Tenant::create();

pest()->artisan("{$command} --skip-tenants={$tenant1->getTenantKey()} --skip-tenants={$tenant2->getTenantKey()}");

tenancy()->initialize($tenant1);

expect(Schema::hasTable('users'))->toBeFalse();

tenancy()->initialize($tenant2);

expect(Schema::hasTable('users'))->toBeFalse();

tenancy()->initialize($tenant3);

expect(Schema::hasTable('users'))->toBeTrue();
})->with([
'tenants:migrate',
'tenants:migrate-fresh',
]);
Comment thread
stancl marked this conversation as resolved.

test('run command can skip specified tenants', function () {
$tenant1 = Tenant::create()->getTenantKey();
$tenant2 = Tenant::create()->getTenantKey();
$tenant3 = Tenant::create()->getTenantKey();

pest()->artisan("tenants:run --skip-tenants=$tenant1 --skip-tenants=$tenant2 'bar foo foo@bar foobar arg --option=option'")
->doesntExpectOutputToContain("Tenant: $tenant1")
->doesntExpectOutputToContain("Tenant: $tenant2")
->expectsOutputToContain("Tenant: $tenant3")
->assertExitCode(0);
});

test('tenants and skip-tenants options can be used together', function () {
$tenant1 = Tenant::create()->getTenantKey();
$tenant2 = Tenant::create()->getTenantKey();
$tenant3 = Tenant::create()->getTenantKey();

// Scope to tenant1+tenant2, then skip tenant2 — only tenant1 should run
pest()->artisan("tenants:run --tenants=$tenant1 --tenants=$tenant2 --skip-tenants=$tenant2 'bar foo foo@bar foobar arg --option=option'")
->expectsOutputToContain("Tenant: $tenant1")
->doesntExpectOutputToContain("Tenant: $tenant2")
->doesntExpectOutputToContain("Tenant: $tenant3")
->assertExitCode(0);
});
Loading