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. Load domain-specific .env, storage path and database/config per customer, enabling clean multi-domain deployments without duplicating the app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. 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
    
  2. Add a Domain:

    php artisan domain:add site1.com
    

    This creates:

    • .env.site1.com
    • storage/site1_com/
    • Entry in config/domains.php
  3. Verify Setup:

    php artisan domain:list
    

    Outputs domain list with paths and .env files.


First Use Case

Run a Domain-Specific Artisan Command:

php artisan config:cache --domain=site1.com

Generates config-site1_com.php with merged configs for site1.com.


Implementation Patterns

Core Workflow

  1. Domain Detection:

    • Uses $_SERVER['SERVER_NAME'] by default (customizable via domain_detection_function_web in Application constructor).
    • Access current domain via app()->domain() or app('domain').
  2. Environment Isolation:

    • Load .env.{domain} (e.g., .env.site1.com) for HTTP requests.
    • CLI/Artisan commands use --domain flag to specify environment:
      php artisan migrate --domain=site1.com
      
  3. Storage Isolation:

    • Each domain gets storage/{sanitized_domain}/ (e.g., storage/site1_com/).
    • Use Storage::disk('local')->path() with domain-specific paths.
  4. Configuration Merging:

    • Domain-specific configs in config/domains/{sanitized_domain}.php (e.g., config/domains/site1_com.php).
    • Merged recursively after config/*.php loading.

Integration Tips

  1. 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);
    }
    
  2. 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}"),
        ],
    ];
    
  3. Queue Workers:

    • Use --domain and --queue flags for isolated workers:
      php artisan queue:work --domain=site1.com --queue=site1_queue
      
    • Configure queue.php to use domain-specific queues:
      'connections' => [
          'site1_queue' => [
              'driver' => 'database',
              'table' => 'jobs_site1',
              'queue' => 'site1_queue',
          ],
      ],
      
  4. Horizon Integration:

    • Replace HorizonApplicationServiceProvider in app/Providers/HorizonServiceProvider.php:
      use Gecche\Multidomain\Horizon\HorizonApplicationServiceProvider;
      
  5. Dynamic Routes:

    Route::domain('{domain}')->group(function () {
        // Domain-specific routes
    });
    

    (Note: Requires custom middleware or package extensions for full support.)


Gotchas and Tips

Pitfalls

  1. $_SERVER['SERVER_NAME'] Missing:

    • Some environments (e.g., CLI, Docker, or custom setups) may not set SERVER_NAME.
    • Fix: Customize detection in bootstrap/app.php:
      $domainParams = [
          'domain_detection_function_web' => fn() => $_SERVER['HTTP_HOST'] ?? 'default',
      ];
      
  2. Queue Driver Conflicts:

    • Shared databases with multiple domains require distinct queues to avoid job mixing.
    • Fix: Use separate queue tables/connections per domain (e.g., jobs_site1, jobs_site2).
  3. Storage Link Hardcoding:

    • storage:link always creates public/storage; no built-in domain support.
    • Workaround: Manually create symlinks:
      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', ''),
      ],
      
  4. Cached Configs:

    • config:cache --domain=site1.com generates config-site1_com.php.
    • Tip: Clear cached configs when config/domains/*.php changes:
      php artisan config:clear
      
  5. Artisan Command Overrides:

    • Some commands (e.g., queue:work) may not pass --domain correctly.
    • Debug: Check app()->domain() in command handlers to verify isolation.

Debugging Tips

  1. Check Current Domain:

    dd(app()->domain()); // Outputs current domain (e.g., "site1.com")
    
  2. Verify Environment Loading:

    dd(env('APP_ENV')); // Should reflect .env.{domain}
    
  3. Inspect Domain List:

    dd(app('domain')->domainList()); // Shows all registered domains
    
  4. Storage Paths:

    dd(storage_path('site1_com')); // Verify path isolation
    

Extension Points

  1. Custom Domain Detection:

    • Override domain_detection_function_web in Application constructor for non-standard setups (e.g., subpaths, headers).
  2. Dynamic Config Merging:

    • Extend Gecche\Multidomain\Foundation\Application to add pre/post-merge hooks for domain configs.
  3. Artisan Command Hooks:

    • Use Laravel’s Illuminate\Console\Events\ArtisanStarting to inject domain logic into commands.
  4. Event Listeners:

    • Listen to Gecche\Multidomain\Events\DomainDetected to react to domain changes:
      public function handle(DomainDetected $event) {
          // Log or act on domain changes
      }
      
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.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata