How to make Laravel jobs tenant aware?

A few months ago, I was looking to build a Multi-Tenant solution with Laravel.
I stumbled on a great course on building a multi-database application from Alex Garrett at CodeCourse.

This multi-tenant solution will identify tenants based on a session id, instead of subdomains. The user will always stay at the same domain and you do not need to display any id parameters in the URL.

However, when completing the course, I found that jobs are tenant aware. So how could we make these jobs tenant aware?

Lets start!

Create an event TenantQueueIdentified and a listener SetTenantQueueConnection. Add these two to your EventServiceProvider.

// App\Providers\EventServiceProvider.php

        'App\Events\Tenant\TenantQueueIdentified'=> [
            'App\Listeners\Tenant\SetTenantQueueConnection',
        ],

In your TenantQueueIdentified event add the folowing code:

// App/Events/Tenant/TenantQueueIdentified.php

public $tenant;

    /**
     * Create a new event instance.
     *
     * @param Tenant $tenant
     */
    public function __construct(Tenant $tenant)
    {
        $this->tenant = $tenant;
    }

Now to make this work, create the following handle() method in your listener. Since the database config is cached, you need to use purge() to set the tenant connection details on each job payload.

// App\Listeners\Tenant\SetTenantQueueConnection.php

    /**
     * Handle the event.
     *
     * @param TenantQueueIdentified $event
     * @return void
     */
    public function handle(TenantQueueIdentified $event)
    {
        app(Manager::class)->setTenant($event->tenant);

        $this->db->createConnection($event->tenant);
        $this->db->purge();

    }

Add the following code to your boot() method within your TenantServiceProvider:

The first part will add the tenant ID to each payload of the dispatched job.

The second part will resolve and set the tenant database connection for the job that is processed. Caution: in this example it uses the current logged in users tenant ID. You may need to modify this logic to your own requirements.

// App\Providers\Tenant\TenantServiceProvider.php

public function boot()
{

..... 

$this->app['queue']->createPayloadUsing(function () {
            return [
                'tenant_id' => session('tenant')
            ];
        });

$this->app['events']->listen(\Illuminate\Queue\Events\JobProcessing::class, function ($event) {
            if (isset($event->job->payload()['tenant_id'])) {
                $tenant = $this->resolveTenant($event->job->payload()['tenant_id']);
                event(new TenantQueueIdentified($tenant));
            }
        });

}

Below the boot() method in your TenantServiceProvider add the following method:

// App\Providers\Tenant\TenantServiceProvider.php

protected function resolveTenant($uuid)
    {
        return Company::where('uuid', $uuid)->first();
    }

IMPORTANT: Make sure your “jobs” and “failed_jobs” tables are within the main database.

Sources:
https://codecourse.com/courses/laravel-tenancy-multi-database
https://divinglaravel.com/understanding-how-laravel-configures-database-connections

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *