Hi everyone,
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.com uses the spain_db.
india.example.com uses the india_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 handle method, 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 JobProcessing Event:
I created a listener for the JobProcessing event 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!