Skip to content

Commit c376185

Browse files
authored
Merge pull request #492 from masterix21/fix-queued-closures
Add support to queued closures
2 parents 5985146 + 3f68dee commit c376185

File tree

8 files changed

+119
-20
lines changed

8 files changed

+119
-20
lines changed

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- laravel: 9.*
1919
testbench: 7.*
2020
- laravel: 8.*
21-
testbench: ^6.23
21+
testbench: ^6.35
2222
exclude:
2323
- laravel: 10.*
2424
php: 8.0

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"require-dev": {
2424
"laravel/legacy-factories": "^1.0.4",
2525
"laravel/octane": "^1.0",
26+
"laravel/serializable-closure": "^1.1",
2627
"mockery/mockery": "^1.4",
2728
"orchestra/testbench": "^6.23|^7.0|^8.0",
2829
"pestphp/pest": "^1.22",

config/multitenancy.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
use Illuminate\Events\CallQueuedListener;
55
use Illuminate\Mail\SendQueuedMailable;
66
use Illuminate\Notifications\SendQueuedNotifications;
7+
use Illuminate\Queue\CallQueuedClosure;
78
use Spatie\Multitenancy\Actions\ForgetCurrentTenantAction;
89
use Spatie\Multitenancy\Actions\MakeQueueTenantAwareAction;
910
use Spatie\Multitenancy\Actions\MakeTenantCurrentAction;
@@ -95,6 +96,7 @@
9596
'queueable_to_job' => [
9697
SendQueuedMailable::class => 'mailable',
9798
SendQueuedNotifications::class => 'notification',
99+
CallQueuedClosure::class => 'closure',
98100
CallQueuedListener::class => 'class',
99101
BroadcastEvent::class => 'event',
100102
],

docs/basic-usage/making-queues-tenant-aware.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ or, using the config `multitenancy.php`:
5555
],
5656
```
5757

58+
## Queueing Closures
59+
60+
Dispatch a closure is slightly different from a job class because here, you can't implement `TenantAware` or `NotTenantAware` interfaces. The package can handle the queue closures by enabling the `queues_are_tenant_aware_by_default`, but if you enjoy keeping to `false` parameter, you can dispatch a tenant-aware job closure like so:
61+
62+
```php
63+
$tenant = Tenant::current();
64+
65+
dispatch(function () use ($tenant) {
66+
$tenant->execute(function () {
67+
// Your job
68+
});
69+
});
70+
```
5871

5972
## When the tenant cannot be retrieved
6073

docs/installation/base-installation.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use Illuminate\Broadcasting\BroadcastEvent;
2626
use Illuminate\Events\CallQueuedListener;
2727
use Illuminate\Mail\SendQueuedMailable;
2828
use Illuminate\Notifications\SendQueuedNotifications;
29+
use Illuminate\Queue\CallQueuedClosure;
2930
use Spatie\Multitenancy\Actions\ForgetCurrentTenantAction;
3031
use Spatie\Multitenancy\Actions\MakeQueueTenantAwareAction;
3132
use Spatie\Multitenancy\Actions\MakeTenantCurrentAction;
@@ -55,7 +56,9 @@ return [
5556
* A valid task is any class that implements Spatie\Multitenancy\Tasks\SwitchTenantTask
5657
*/
5758
'switch_tenant_tasks' => [
58-
// add tasks here
59+
// \Spatie\Multitenancy\Tasks\PrefixCacheTask::class,
60+
// \Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask::class,
61+
// \Spatie\Multitenancy\Tasks\SwitchRouteCacheTask::class,
5962
],
6063

6164
/*
@@ -89,8 +92,14 @@ return [
8992
*/
9093
'current_tenant_container_key' => 'currentTenant',
9194

95+
/**
96+
* Set it to `true` if you like to cache the tenant(s) routes
97+
* in a shared file using the `SwitchRouteCacheTask`.
98+
*/
99+
'shared_routes_cache' => false,
100+
92101
/*
93-
* You can customize some of the behavior of this package by using our own custom action.
102+
* You can customize some of the behavior of this package by using your own custom action.
94103
* Your custom action should always extend the default one.
95104
*/
96105
'actions' => [
@@ -109,9 +118,24 @@ return [
109118
'queueable_to_job' => [
110119
SendQueuedMailable::class => 'mailable',
111120
SendQueuedNotifications::class => 'notification',
121+
CallQueuedClosure::class => 'closure',
112122
CallQueuedListener::class => 'class',
113123
BroadcastEvent::class => 'event',
114124
],
125+
126+
/*
127+
* Jobs tenant aware even if these don't implement the TenantAware interface.
128+
*/
129+
'tenant_aware_jobs' => [
130+
// ...
131+
],
132+
133+
/*
134+
* Jobs not tenant aware even if these don't implement the NotTenantAware interface.
135+
*/
136+
'not_tenant_aware_jobs' => [
137+
// ...
138+
],
115139
];
116140
```
117141

tests/Feature/Commands/TenantAwareCommandTest.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,11 @@
44
use Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask;
55

66
beforeEach(function () {
7-
config(['database.default' => 'tenant']);
87
config()->set('multitenancy.switch_tenant_tasks', [SwitchTenantDatabaseTask::class]);
98

109
$this->tenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_1']);
11-
$this->tenant->makeCurrent();
1210

1311
$this->anotherTenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_2']);
14-
$this->anotherTenant->makeCurrent();
15-
16-
Tenant::forgetCurrent();
1712
});
1813

1914
it('fails with a non-existing tenant')

tests/Feature/Commands/TenantsArtisanCommandTest.php

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,26 @@
55
use Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask;
66

77
beforeEach(function () {
8-
config(['database.default' => 'tenant']);
9-
108
config()->set('multitenancy.switch_tenant_tasks', [SwitchTenantDatabaseTask::class]);
119

1210
$this->tenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_1']);
13-
$this->tenant->makeCurrent();
14-
Schema::connection('tenant')->dropIfExists('migrations');
11+
$this->tenant->execute(fn () => Schema::connection('tenant')->dropIfExists('migrations'));
1512

1613
$this->anotherTenant = Tenant::factory()->create(['database' => 'laravel_mt_tenant_2']);
17-
$this->anotherTenant->makeCurrent();
18-
Schema::connection('tenant')->dropIfExists('migrations');
19-
20-
Tenant::forgetCurrent();
14+
$this->anotherTenant->execute(fn () => Schema::connection('tenant')->dropIfExists('migrations'));
2115
});
2216

2317
it('can migrate all tenant databases', function () {
2418
$this
25-
->artisan('tenants:artisan migrate')
19+
->artisan('tenants:artisan "migrate --database=tenant"')
2620
->assertExitCode(0);
2721

2822
assertTenantDatabaseHasTable($this->tenant, 'migrations');
2923
assertTenantDatabaseHasTable($this->anotherTenant, 'migrations');
3024
});
3125

3226
it('can migrate a specific tenant', function () {
33-
$this->artisan('tenants:artisan migrate --tenant="' . $this->anotherTenant->id . '"')->assertExitCode(0);
27+
$this->artisan('tenants:artisan "migrate --database=tenant" --tenant="' . $this->anotherTenant->id . '"')->assertExitCode(0);
3428

3529
assertTenantDatabaseDoesNotHaveTable($this->tenant, 'migrations');
3630
assertTenantDatabaseHasTable($this->anotherTenant, 'migrations');
@@ -40,7 +34,7 @@
4034
config(['multitenancy.tenant_artisan_search_fields' => 'domain']);
4135

4236
$this->artisan('tenants:artisan', [
43-
'artisanCommand' => 'migrate',
37+
'artisanCommand' => 'migrate --database=tenant',
4438
'--tenant' => $this->anotherTenant->id,
4539
])
4640
->expectsOutput("No tenant(s) found.")
@@ -51,7 +45,7 @@
5145
config(['multitenancy.tenant_artisan_search_fields' => 'domain']);
5246

5347
$this->artisan('tenants:artisan', [
54-
'artisanCommand' => 'migrate',
48+
'artisanCommand' => 'migrate --database=tenant',
5549
'--tenant' => $this->anotherTenant->domain,
5650
])->assertExitCode(0);
5751

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
use Spatie\Multitenancy\Models\Tenant;
4+
use Spatie\Valuestore\Valuestore;
5+
6+
beforeEach(function () {
7+
config()->set('multitenancy.queues_are_tenant_aware_by_default', false);
8+
9+
$this->tenant = Tenant::factory()->create();
10+
});
11+
12+
it('succeeds with closure job when queues are tenant aware by default', function () {
13+
$valuestore = Valuestore::make(tempFile('tenantAware.json'))->flush();
14+
15+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
16+
17+
$this->tenant->makeCurrent();
18+
19+
dispatch(function () use ($valuestore) {
20+
$tenant = Tenant::current();
21+
22+
$valuestore->put('tenantId', $tenant?->getKey());
23+
$valuestore->put('tenantName', $tenant?->name);
24+
});
25+
26+
$this->artisan('queue:work --once')->assertExitCode(0);
27+
28+
expect($valuestore->get('tenantId'))->toBe($this->tenant->getKey())
29+
->and($valuestore->get('tenantName'))->toBe($this->tenant->name);
30+
});
31+
32+
it('fails with closure job when queues are not tenant aware by default', function () {
33+
$valuestore = Valuestore::make(tempFile('tenantAware.json'))->flush();
34+
35+
$this->tenant->makeCurrent();
36+
37+
dispatch(function () use ($valuestore) {
38+
$tenant = Tenant::current();
39+
40+
$valuestore->put('tenantId', $tenant?->getKey());
41+
$valuestore->put('tenantName', $tenant?->name);
42+
});
43+
44+
$this->artisan('queue:work --once')->assertExitCode(0);
45+
46+
expect($valuestore->get('tenantId'))->toBeNull()
47+
->and($valuestore->get('tenantName'))->toBeNull();
48+
});
49+
50+
it('succeeds with closure job when a tenant is specified', function () {
51+
$valuestore = Valuestore::make(tempFile('tenantAware.json'))->flush();
52+
53+
$currentTenant = $this->tenant;
54+
55+
dispatch(function () use ($valuestore, $currentTenant) {
56+
$currentTenant->makeCurrent();
57+
58+
$tenant = Tenant::current();
59+
60+
$valuestore->put('tenantId', $tenant?->getKey());
61+
$valuestore->put('tenantName', $tenant?->name);
62+
63+
$currentTenant->forget();
64+
});
65+
66+
$this->artisan('queue:work --once')->assertExitCode(0);
67+
68+
expect($valuestore->get('tenantId'))->toBe($this->tenant->getKey())
69+
->and($valuestore->get('tenantName'))->toBe($this->tenant->name);
70+
});

0 commit comments

Comments
 (0)