diff --git a/config/multitenancy.php b/config/multitenancy.php
index 5e4faa8..59d239f 100644
--- a/config/multitenancy.php
+++ b/config/multitenancy.php
@@ -1,5 +1,10 @@
MigrateTenantAction::class,
],
+ /*
+ * You can customize the way in which the package resolves the queueable to a job.
+ *
+ * For example, using the package laravel-actions (by Loris Leiva), you can
+ * resolve JobDecorator to getAction() like so: JobDecorator::class => 'getAction'
+ */
+ 'queueable_to_job' => [
+ SendQueuedMailable::class => 'mailable',
+ SendQueuedNotifications::class => 'notification',
+ CallQueuedClosure::class => 'closure',
+ CallQueuedListener::class => 'class',
+ BroadcastEvent::class => 'event',
+ ],
+
/*
* Jobs tenant aware even if these don't implement the TenantAware interface.
*/
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index d277999..fea497a 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -9,7 +9,7 @@
-
+
diff --git a/src/Actions/MakeQueueTenantAwareAction.php b/src/Actions/MakeQueueTenantAwareAction.php
index 1d22229..b940dd8 100644
--- a/src/Actions/MakeQueueTenantAwareAction.php
+++ b/src/Actions/MakeQueueTenantAwareAction.php
@@ -2,8 +2,12 @@
namespace Spatie\Multitenancy\Actions;
+use Illuminate\Mail\SendQueuedMailable;
+use Illuminate\Notifications\SendQueuedNotifications;
+use Illuminate\Queue\CallQueuedClosure;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Events\JobRetryRequested;
+use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Context;
use Spatie\Multitenancy\Concerns\BindAsCurrentTenant;
use Spatie\Multitenancy\Concerns\UsesMultitenancyConfig;
@@ -44,9 +48,13 @@ protected function listenForJobsRetryRequested(): static
protected function isTenantAware(JobProcessing|JobRetryRequested $event): bool
{
- $jobName = $this->getEventPayload($event)['data']['commandName'];
+ $payload = $this->getEventPayload($event);
- $reflection = new \ReflectionClass($jobName);
+ $command = unserialize($payload['data']['command']);
+
+ $job = $this->getJobFromQueueable($command);
+
+ $reflection = new \ReflectionClass($job);
if ($reflection->implementsInterface(TenantAware::class)) {
return true;
@@ -67,6 +75,21 @@ protected function isTenantAware(JobProcessing|JobRetryRequested $event): bool
return config('multitenancy.queues_are_tenant_aware_by_default') === true;
}
+ protected function getJobFromQueueable(object $queueable)
+ {
+ $job = Arr::get(config('multitenancy.queueable_to_job'), $queueable::class);
+
+ if (! $job) {
+ return $queueable;
+ }
+
+ if (method_exists($queueable, $job)) {
+ return $queueable->{$job}();
+ }
+
+ return $queueable->$job;
+ }
+
protected function getEventPayload($event): ?array
{
return match (true) {
diff --git a/tests/Feature/TenantAwareJobs/QueuedBroadcastEventTest.php b/tests/Feature/TenantAwareJobs/QueuedBroadcastEventTest.php
new file mode 100644
index 0000000..e890535
--- /dev/null
+++ b/tests/Feature/TenantAwareJobs/QueuedBroadcastEventTest.php
@@ -0,0 +1,55 @@
+set('multitenancy.queues_are_tenant_aware_by_default', true);
+ config()->set('queue.default', 'sync');
+ config()->set('mail.default', 'log');
+
+ $this->tenant = Tenant::factory()->create();
+});
+
+it('will fail when no tenant is present and listeners are tenant aware by default', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ Event::listen(TestEvent::class, ListenerTenantAware::class);
+
+ Broadcast::event(new BroadcastTenantAware("Hello world!"));
+})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
+
+it('will not fail when no tenant is present and listeners are tenant aware by default', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ Event::listen(TestEvent::class, ListenerNotTenantAware::class);
+ Broadcast::event(new BroadcastNotTenantAware("Hello world!"));
+
+ $this->expectExceptionMessage("Method Illuminate\Events\Dispatcher::assertDispatchedTimes does not exist.");
+
+ Event::assertDispatchedTimes(TestEvent::class);
+});
+
+it('will inject the current tenant id', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ $this->tenant->makeCurrent();
+
+ Event::listen(TestEvent::class, ListenerNotTenantAware::class);
+
+ expect(
+ Broadcast::event(new BroadcastTenantAware("Hello world!"))
+ )->toBeInstanceOf(\Illuminate\Broadcasting\PendingBroadcast::class);
+});
diff --git a/tests/Feature/TenantAwareJobs/QueuedListenerTest.php b/tests/Feature/TenantAwareJobs/QueuedListenerTest.php
new file mode 100644
index 0000000..9b1652e
--- /dev/null
+++ b/tests/Feature/TenantAwareJobs/QueuedListenerTest.php
@@ -0,0 +1,52 @@
+set('multitenancy.queues_are_tenant_aware_by_default', true);
+ config()->set('queue.default', 'sync');
+ config()->set('mail.default', 'log');
+
+ $this->tenant = Tenant::factory()->create();
+});
+
+it('will fail when no tenant is present and listeners are tenant aware by default', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ Event::listen(TestEvent::class, ListenerTenantAware::class);
+
+ Event::dispatch(new TestEvent("Hello world!"));
+})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
+
+it('will not fail when no tenant is present and listeners are tenant aware by default', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ Event::listen(TestEvent::class, ListenerNotTenantAware::class);
+ Event::dispatch(new TestEvent("Hello world!"));
+
+ $this->expectExceptionMessage("Method Illuminate\Events\Dispatcher::assertDispatchedTimes does not exist.");
+
+ Event::assertDispatchedTimes(TestEvent::class);
+});
+
+it('will inject the current tenant id', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ $this->tenant->makeCurrent();
+
+ Event::listen(TestEvent::class, ListenerNotTenantAware::class);
+
+ expect(
+ Event::dispatch(new TestEvent("Hello world!"))
+ )->toEqual([0 => null]);
+});
diff --git a/tests/Feature/TenantAwareJobs/QueuedMailableTest.php b/tests/Feature/TenantAwareJobs/QueuedMailableTest.php
index d7eb69a..a94223f 100644
--- a/tests/Feature/TenantAwareJobs/QueuedMailableTest.php
+++ b/tests/Feature/TenantAwareJobs/QueuedMailableTest.php
@@ -3,6 +3,7 @@
use Illuminate\Support\Facades\Mail;
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
use Spatie\Multitenancy\Models\Tenant;
+use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableTenantAware;
beforeEach(function () {
@@ -19,6 +20,16 @@
Mail::to('test@spatie.be')->queue(new MailableTenantAware());
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
+it('will not fail when no tenant is present and mailables are tenant aware by default', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ Mail::to('test@spatie.be')->queue(new MailableNotTenantAware());
+
+ $this->expectExceptionMessage("Method Illuminate\Mail\Mailer::assertSentCount does not exist.");
+
+ Mail::assertSentCount(1);
+});
+
it('will inject the current tenant id', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
diff --git a/tests/Feature/TenantAwareJobs/QueuedNotificationsTest.php b/tests/Feature/TenantAwareJobs/QueuedNotificationsTest.php
index 1cfba24..0b07f0a 100644
--- a/tests/Feature/TenantAwareJobs/QueuedNotificationsTest.php
+++ b/tests/Feature/TenantAwareJobs/QueuedNotificationsTest.php
@@ -3,6 +3,7 @@
use Illuminate\Support\Facades\Notification;
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
use Spatie\Multitenancy\Tests\Feature\Models\TenantNotifiable;
+use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\NotificationNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\NotificationTenantAware;
beforeEach(function () {
@@ -21,6 +22,16 @@
Notification::assertNothingSent();
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
+it('will not fail when no tenant is present and mailables are tenant aware by default', function () {
+ config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
+
+ $this->tenant->notify((new NotificationNotTenantAware()));
+
+ $this->expectExceptionMessage("Call to undefined method Illuminate\Notifications\Channels\MailChannel::assertCount()");
+
+ Notification::assertCount(1);
+});
+
it('will inject the current tenant id', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
diff --git a/tests/Feature/TenantAwareJobs/TestClasses/BroadcastNotTenantAware.php b/tests/Feature/TenantAwareJobs/TestClasses/BroadcastNotTenantAware.php
new file mode 100644
index 0000000..36b587f
--- /dev/null
+++ b/tests/Feature/TenantAwareJobs/TestClasses/BroadcastNotTenantAware.php
@@ -0,0 +1,24 @@
+view('mailable');
+ }
+}
diff --git a/tests/Feature/TenantAwareJobs/TestClasses/NotificationNotTenantAware.php b/tests/Feature/TenantAwareJobs/TestClasses/NotificationNotTenantAware.php
new file mode 100644
index 0000000..4383a37
--- /dev/null
+++ b/tests/Feature/TenantAwareJobs/TestClasses/NotificationNotTenantAware.php
@@ -0,0 +1,33 @@
+subject('Message')
+ ->greeting('Hello!')
+ ->line('Say goodbye!');
+ }
+
+ public function toArray($notifiable)
+ {
+ return [ ];
+ }
+}
diff --git a/tests/Feature/TenantAwareJobs/TestClasses/TestEvent.php b/tests/Feature/TenantAwareJobs/TestClasses/TestEvent.php
new file mode 100644
index 0000000..77e743c
--- /dev/null
+++ b/tests/Feature/TenantAwareJobs/TestClasses/TestEvent.php
@@ -0,0 +1,16 @@
+