Destroy the (Laravel) Environment

Destroy the (Laravel) Environment
Photo by Muhammad Numan / Unsplash

Context: I have a Laravel multi tenancy application, with different .env files loaded according the current accessed subdomain and containing many parameters (the database configuration for each instance, but also many other items). This works when you access the PHP application via web, and booting the app from scratch at every request, but doesn't work with queued jobs, as the queue worker is bootstrapped once - with the default .env - until you have different queue workers, one for each instance (which was not a viable solution for my 100+ instances).

I've struggled a lot to understand how to "reboot" the core Laravel app with the correct .env for each different incoming job. To the point I've preferred for some time to handle queued jobs as local HTTP requests, so handling them exactly as any other web request. But, at the end, I've found the solution.

This is the core behavior:

use Illuminate\Support\Env;
use Illuminate\Support\Facades\URL;
use Illuminate\Foundation\Bootstrap\LoadConfiguration;
use Dotenv\Dotenv;

// Take the current DB connection name, to be purged later
$start_connection = env('DB_CONNECTION');

// Configures the desired new .env path
app()->loadEnvironmentFrom('.env.myotherenv');

// Reloads the .env file
Dotenv::create(Env::getRepository(), app()->environmentPath(), app()->environmentFile())->load();

// Forces a reload of the configuration
(new LoadConfiguration())->bootstrap(app());

// Restarts the DB connection, with the new parameters
app('db')->purge($start_connection);

// Optional: forces the domain for URLs generated with route()
URL::forceRootUrl(env('APP_URL'));

This can be executed at the beginning of every job handle() phase, assumed that the path to the right .env file has been serialized into the job's payload (can be done using a custom Job class to be extended by every job implementation, or intercepting the proper events to alter the payload on the go).