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 a domain-specific .env, storage path, and database/config per customer, enabling isolated data and settings while sharing the same application and deployment.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require gecche/laravel-multidomain:13.*
    

    Replace 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 example.com
    

    This creates:

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

    php artisan domain:list
    

    Check if example.com appears in the output.

First Use Case

Run a domain-specific Artisan command:

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

This generates config-example_com.php in the root directory.


Implementation Patterns

Core Workflows

  1. Domain-Specific Configuration:

    • Use domain() helper in controllers/services to access the current domain:
      $currentDomain = app()->domain();
      
    • Access domain list programmatically:
      $domains = app()->domainList();
      
  2. Environment-Specific Logic:

    • Override .env.example.com for domain-specific settings (e.g., DB_DATABASE, APP_URL).
    • Use env() as usual; the package auto-loads the correct .env file.
  3. Storage Isolation:

    • Files uploaded via Laravel’s Storage facade are saved to storage/{domain}_com/.
    • Example:
      Storage::disk('public')->put('file.txt', 'content');
      // Saves to: storage/example_com/app/public/file.txt
      
  4. Domain-Specific Artisan Commands:

    • Pass --domain to any Artisan command:
      php artisan migrate --domain=example.com
      
    • Useful for running migrations, queues, or cache updates per domain.
  5. Queue Management:

    • Configure QUEUE_DEFAULT per domain in .env.example.com.
    • Run workers per domain:
      php artisan queue:work --domain=example.com --queue=domain_queue
      

Integration Tips

  • Middleware: Use the domain() helper in middleware to enforce domain-specific logic:
    public function handle(Request $request, Closure $next) {
        $domain = app()->domain();
        if ($domain !== 'example.com') {
            abort(403);
        }
        return $next($request);
    }
    
  • Service Providers: Bind domain-specific services in boot():
    public function boot() {
        $domain = app()->domain();
        $this->app->bind('domainService', function () use ($domain) {
            return new DomainService($domain);
        });
    }
    
  • Views: Pass the current domain to Blade:
    @inject('domain', 'Illuminate\Contracts\Foundation\Application')
    Current Domain: {{ $domain->domain() }}
    

Gotchas and Tips

Pitfalls

  1. $_SERVER['SERVER_NAME'] Issues:

    • The package defaults to $_SERVER['SERVER_NAME'] for domain detection. If this is empty (e.g., CLI or non-standard environments), override it in bootstrap/app.php:
      $domainParams = [
          'domain_detection_function_web' => function() {
              return $_SERVER['HTTP_HOST'] ?? 'default.com';
          }
      ];
      return Application::configure(..., domainParams: $domainParams)->create();
      
  2. Storage Linking:

    • The storage:link command does not support domain-specific symlinks. Manually create links:
      ln -s storage/example_com/app/public public/storage-example_com
      
    • Update filesystems.php:
      'public' => [
          'url' => env('APP_URL').'/storage'.env('APP_PUBLIC_STORAGE', ''),
      ],
      
    • Set APP_PUBLIC_STORAGE=-example_com in .env.example.com.
  3. Queue Conflicts:

    • If using the database queue driver, ensure each domain has a unique queue name (e.g., QUEUE_DEFAULT=example_com_queue). Avoid sharing queues across domains.
  4. Horizon Integration:

    • Replace HorizonApplicationServiceProvider in app/Providers/HorizonServiceProvider.php:
      use Gecche\Multidomain\Horizon\HorizonApplicationServiceProvider;
      
  5. Environment File Permissions:

    • Ensure .env.example.com files are readable by the web server (e.g., chmod 644 .env.example.com).

Debugging Tips

  • Check Current Domain: Add this to a route or middleware to debug:
    dd(app()->domain(), app()->domainList());
    
  • Verify Config Loading: Run php artisan config:cache --domain=example.com and check for config-example_com.php.
  • Storage Paths: Use storage_path() to confirm paths:
    dd(storage_path('example_com'));
    
  • Artisan Domain Flag: Always include --domain for domain-specific commands. Omitting it defaults to the primary domain.

Extension Points

  1. Custom Domain Detection: Extend the domain_detection_function_web in bootstrap/app.php for non-standard setups (e.g., subdomains, headers):

    'domain_detection_function_web' => function() {
        return $_SERVER['HTTP_X_CUSTOM_DOMAIN'] ?? $_SERVER['SERVER_NAME'];
    }
    
  2. Dynamic Domain Routing: Use middleware to route based on the domain:

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

    Combine with the package’s domain() helper for logic.

  3. Domain-Specific Packages: Load domain-specific packages in config/app.php:

    'providers' => [
        // ...
        \Vendor\DomainSpecific\ExampleProvider::class => app()->domain() === 'example.com',
    ],
    
  4. Event Listeners: Listen for domain-specific events (e.g., domain.example.com):

    Event::listen('domain.example.com', function () {
        // Logic for example.com
    });
    
  5. Testing: Mock the domain in tests:

    $this->app->instance('domain', 'test.com');
    

    Or use the --domain flag in artisan tests:

    $this->artisan('domain:list --domain=test.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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope