Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Multidomain Laravel Package

gecche/laravel-multidomain

Run one Laravel codebase across multiple domains/tenants. Automatically load per-domain .env, storage paths, and database connections so each customer can have isolated config, data, and files while sharing the same application code.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require gecche/laravel-multidomain:13.*
    

    Update bootstrap/app.php to replace Illuminate\Foundation\Application with Gecche\Multidomain\Foundation\Application. Override QueueServiceProvider in config/app.php:

    'providers' => [...]->replace([
        \Illuminate\Queue\QueueServiceProvider::class => \Gecche\Multidomain\Queue\QueueServiceProvider::class,
    ])->merge([...])->toArray(),
    

    Publish the config:

    php artisan vendor:publish
    
  2. Add a Domain:

    php artisan domain:add site1.com
    

    This creates:

    • .env.site1.com (custom env file)
    • storage/site1_com/ (domain-specific storage)
    • Updates config/domains.php.
  3. Verify Setup:

    php artisan domain:list
    

    Check the current domain in code:

    $currentDomain = app()->domain();
    

First Use Case

Multi-Tenant API Endpoint: Create a route in routes/api.php:

Route::get('/tenant-data', function () {
    $domain = app()->domain();
    return response()->json([
        'domain' => $domain,
        'storage_path' => storage_path("{$domain}_com"),
        'env_file' => ".env.{$domain}",
    ]);
});

Test with curl http://site1.com/api/tenant-data.


Implementation Patterns

Core Workflows

  1. Domain-Specific Configuration:

    • Use app()->domain() to dynamically load domain-specific .env files.
    • Override Laravel’s config() helper to read from the correct config file (e.g., config-site1_com.php):
      $value = config('database.connections.mysql.host', null, $domain);
      
  2. Storage Isolation:

    • Access domain-specific storage:
      $domainStorage = storage_path("{$domain}_com/uploads");
      
    • Use Storage::disk('local')->put() with domain-aware paths.
  3. Middleware for Tenant Context:

    public function handle(Request $request, Closure $next) {
        $domain = app()->domain();
        config(['app.tenant' => $domain]);
        return $next($request);
    }
    
  4. Artisan Commands with Domains:

    php artisan migrate --domain=site1.com
    php artisan queue:work --domain=site2.com --queue=site2-queue
    

Integration Tips

  1. Database Connections: Define dynamic connections in config/database.php:

    'connections' => [
        'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            // Override per-domain
            'database' => env('DB_DATABASE', "db_{$domain}"),
        ],
    ],
    
  2. Queue Workers:

    • Use separate queues per domain (e.g., site1-queue, site2-queue).
    • Configure in .env.site1.com:
      QUEUE_DEFAULT=site1-queue
      
  3. Horizon Integration: Replace HorizonApplicationServiceProvider in app/Providers/HorizonServiceProvider.php:

    use Gecche\Multidomain\Horizon\HorizonApplicationServiceProvider;
    
  4. SPA Public Assets:

    • Symlink domain-specific storage:
      ln -s storage/site1_com/app/public public/storage-site1_com
      
    • Configure filesystems.php:
      'public' => [
          'url' => env('APP_URL').'/storage'.env('APP_PUBLIC_STORAGE', ''),
      ],
      

Gotchas and Tips

Pitfalls

  1. $_SERVER['SERVER_NAME'] Missing:

    • If $_SERVER['SERVER_NAME'] is empty (e.g., CLI or non-standard servers), override detection in bootstrap/app.php:
      $domainParams = [
          'domain_detection_function_web' => fn() => $_SERVER['HTTP_HOST'] ?? 'default',
      ];
      
  2. Config Caching:

    • Cache config per domain:
      php artisan config:cache --domain=site1.com
      
    • Clearing cache requires domain-specific commands:
      php artisan config:clear --domain=site1.com
      
  3. Queue Conflicts:

    • Avoid sharing queues across domains if using the database driver. Use separate tables or queues.
  4. Storage Permissions:

    • Ensure domain-specific storage folders (storage/{domain}_com) are writable:
      chmod -R 775 storage/site1_com
      

Debugging

  1. Check Current Domain:

    dd(app()->domain(), app()->domainList());
    
  2. Verify Env Loading:

    • Temporarily add debug logs in vendor/gecche/laravel-multidomain/src/Foundation/Application.php:
      Log::debug("Loaded env for domain: {$this->domain}");
      
  3. Artisan Domain Issues:

    • If --domain flag is ignored, ensure the QueueServiceProvider is properly overridden.

Extension Points

  1. Custom Domain Detection: Extend Application to support subdomains or path-based routing:

    'domain_detection_function_web' => function() {
        return parse_url($_SERVER['REQUEST_URI'], PHP_URL_HOST);
    },
    
  2. Dynamic Config Loading: Override config() in a service provider:

    Config::addListener(function ($name) {
        if (str_contains($name, 'tenant')) {
            return config("tenant.{$app()->domain()}.{$name}");
        }
    });
    
  3. Domain-Specific Routes: Use middleware to load routes dynamically:

    Route::domain('{domain}', function () {
        // Load domain-specific routes
    })->where('domain', implode('|', array_keys(config('domains'))));
    
  4. CLI Domain Defaults: Set a default domain for CLI in config/domain.php:

    'default_domain' => 'default.com',
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport