gecche/laravel-multidomain
Run one Laravel codebase across multiple domains/tenants. Load domain-specific .env, storage path and database/config per customer, enabling clean multi-domain deployments without duplicating the app.
Installation:
composer require gecche/laravel-multidomain:13.*
Replace Application in bootstrap/app.php:
use Gecche\Multidomain\Foundation\Application;
Override QueueServiceProvider in config/app.php:
'providers' => [...]->replace([
\Illuminate\Queue\QueueServiceProvider::class => \Gecche\Multidomain\Queue\QueueServiceProvider::class,
])->merge([...])->toArray(),
Publish config:
php artisan vendor:publish
Add a Domain:
php artisan domain:add site1.com
This creates:
.env.site1.comstorage/site1_com/config/domains.phpVerify Setup:
php artisan domain:list
Outputs domain list with paths and .env files.
Run a Domain-Specific Artisan Command:
php artisan config:cache --domain=site1.com
Generates config-site1_com.php with merged configs for site1.com.
Domain Detection:
$_SERVER['SERVER_NAME'] by default (customizable via domain_detection_function_web in Application constructor).app()->domain() or app('domain').Environment Isolation:
.env.{domain} (e.g., .env.site1.com) for HTTP requests.--domain flag to specify environment:
php artisan migrate --domain=site1.com
Storage Isolation:
storage/{sanitized_domain}/ (e.g., storage/site1_com/).Storage::disk('local')->path() with domain-specific paths.Configuration Merging:
config/domains/{sanitized_domain}.php (e.g., config/domains/site1_com.php).config/*.php loading.Middleware for Domain Logic:
public function handle(Request $request, Closure $next) {
$domain = app()->domain();
// Domain-specific logic (e.g., redirect, auth, or feature flags)
return $next($request);
}
Dynamic Database Connections:
// In a service provider or config file
'connections' => [
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST'),
// Use domain-specific env vars (e.g., DB_DATABASE_site1_com)
'database' => env("DB_DATABASE_{$domain}"),
],
];
Queue Workers:
--domain and --queue flags for isolated workers:
php artisan queue:work --domain=site1.com --queue=site1_queue
queue.php to use domain-specific queues:
'connections' => [
'site1_queue' => [
'driver' => 'database',
'table' => 'jobs_site1',
'queue' => 'site1_queue',
],
],
Horizon Integration:
HorizonApplicationServiceProvider in app/Providers/HorizonServiceProvider.php:
use Gecche\Multidomain\Horizon\HorizonApplicationServiceProvider;
Dynamic Routes:
Route::domain('{domain}')->group(function () {
// Domain-specific routes
});
(Note: Requires custom middleware or package extensions for full support.)
$_SERVER['SERVER_NAME'] Missing:
SERVER_NAME.bootstrap/app.php:
$domainParams = [
'domain_detection_function_web' => fn() => $_SERVER['HTTP_HOST'] ?? 'default',
];
Queue Driver Conflicts:
jobs_site1, jobs_site2).Storage Link Hardcoding:
storage:link always creates public/storage; no built-in domain support.ln -s storage/site1_com/app/public public/storage-site1
Then configure filesystems.php:
'public' => [
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage'.env('DOMAIN_STORAGE_SUFFIX', ''),
],
Cached Configs:
config:cache --domain=site1.com generates config-site1_com.php.config/domains/*.php changes:
php artisan config:clear
Artisan Command Overrides:
queue:work) may not pass --domain correctly.app()->domain() in command handlers to verify isolation.Check Current Domain:
dd(app()->domain()); // Outputs current domain (e.g., "site1.com")
Verify Environment Loading:
dd(env('APP_ENV')); // Should reflect .env.{domain}
Inspect Domain List:
dd(app('domain')->domainList()); // Shows all registered domains
Storage Paths:
dd(storage_path('site1_com')); // Verify path isolation
Custom Domain Detection:
domain_detection_function_web in Application constructor for non-standard setups (e.g., subpaths, headers).Dynamic Config Merging:
Gecche\Multidomain\Foundation\Application to add pre/post-merge hooks for domain configs.Artisan Command Hooks:
Illuminate\Console\Events\ArtisanStarting to inject domain logic into commands.Event Listeners:
Gecche\Multidomain\Events\DomainDetected to react to domain changes:
public function handle(DomainDetected $event) {
// Log or act on domain changes
}
How can I help you explore Laravel packages today?