I’m working on a multitenant application in Laravel where the database connection is determined dynamically based on the subdomain of the request. For example:
spain.example.comuses thespain_db.india.example.comuses theindia_db.
I’ve implemented middleware (IdentifyTenant) to detect the subdomain, fetch the tenant’s database details, and set the connection dynamically like this:
config([
'database.connections.custom' => [
'driver' => 'mysql',
'host' => $tenantDetails['db_host'],
'database' => $tenantDetails['db_name'],
'username' => $tenantDetails['db_user'],
'password' => $tenantDetails['db_password'],
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
],
]);
DB::setDefaultConnection('custom');
This works perfectly for HTTP requests, and the database connection is dynamically set based on the subdomain.
The Problem: Queue Workers
When I run php artisan queue:work or php artisan queue:listen, the queue worker always picks the default database from the .env file. However, in my case, I need the worker to dynamically determine the database connection based on the tenant details stored in the job.
What I’ve Tried:
Passing Tenant Details in the Job Payload: I included tenant-specific details in the job when dispatching it:
$tenantDetails = [ 'db_host' => '127.0.0.1', 'db_name' => 'spain_db', 'db_user' => 'root', 'db_password' => '', ]; YourJob::dispatch($tenantDetails);In the job’s
handlemethod, I dynamically set the database:config([ 'database.connections.custom' => [ 'driver' => 'mysql', 'host' => $this->tenantDetails['db_host'], 'database' => $this->tenantDetails['db_name'], 'username' => $this->tenantDetails['db_user'], 'password' => $this->tenantDetails['db_password'], 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, 'engine' => null, ], ]); DB::setDefaultConnection('custom');Using the
JobProcessingEvent: I created a listener for theJobProcessingevent to dynamically configure the tenant database:namespace App\Listeners; use Illuminate\Queue\Events\JobProcessing; use Illuminate\Support\Facades\DB; class SetTenantForQueue { public function handle(JobProcessing $event) { $tenantDetails = $event->job->payload()['data']['tenantDetails'] ?? null; if ($tenantDetails) { config([ 'database.connections.custom' => [ 'driver' => 'mysql', 'host' => $tenantDetails['db_host'], 'database' => $tenantDetails['db_name'], 'username' => $tenantDetails['db_user'], 'password' => $tenantDetails['db_password'], 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, 'engine' => null, ], ]); DB::setDefaultConnection('custom'); } } }And registered it in
EventServiceProvider:use Illuminate\Queue\Events\JobProcessing; use App\Listeners\SetTenantForQueue; protected $listen = [ JobProcessing::class => [ SetTenantForQueue::class, ], ];
The Issue:
Despite these attempts, the worker still seems to pick the default database from the .env file when executing the job. How can I ensure that the queue worker dynamically uses the tenant database based on the subdomain or the job payload?
What I Need Help With:
- How can I configure Laravel’s queue worker to dynamically set the database connection for each job?
- Is there a better way to handle multitenant databases with queues in Laravel?
- How can I make the queue worker "tenant-aware" without requiring manual intervention (e.g., running separate workers for each tenant)?
I’d really appreciate any advice or best practices for handling this scenario in a multitenant architecture. Thanks in advance!