spatie/laravel-multitenancy

repository·main·Indexed 23 days ago

https://github.com/spatie/laravel-multitenancy

An unopinionated multitenancy package for Laravel that provides essentials for making applications tenant-aware. It supports both single and multiple database architectures, offering tools for tenant identification, tenant switching, and tenant-aware queued jobs and Artisan commands.

Tokens
17.6K
Snippets
51
Records
96
Agent score
74%

What's inside spatie/laravel-multitenancy

  1. Overview of laravel-multitenancy

    main

    The spatie/laravel-multitenancy package provides the essential tools to make a Laravel application tenant-aware. It is designed to be unopinionated, focusing on the core requirements of multitenancy:

    • Tenant Identification: Determining which tenant should be active for the current request.
    • Tenant Switching: Defining actions that occur when the current tenant is switched.
    • Database Support: Compatible with projects using either a single database or multiple databases for tenants.

    Key features include making queued jobs tenant-aware, providing Artisan commands that run for each tenant, and offering easy ways to set database connections on models.

  2. How tasks prepare the environment during tenant switching

    main

    When a tenant is made the 'current' tenant, the package automatically executes a list of tasks defined in the switch_tenant_tasks key of your multitenancy configuration file.

    These tasks allow you to run custom logic to reconfigure your application environment specifically for the newly active tenant. This is the primary mechanism for handling tenant-specific requirements like database connections, cache prefixes, or filesystem configurations.

    Common use cases include:

    • Switching the database connection to the tenant's specific database.
    • Prefixing the cache to prevent data leakage between tenants.
    • Custom logic for setting up tenant-specific environment variables or services.
  3. Handle state persistence in TenantAware commands

    main

    When using the TenantAware trait, the same command instance is reused for each tenant execution. This means that class properties will retain their values between tenants unless you explicitly reset them at the start of the handle() method.

    To ensure a clean state for every tenant, reset your properties inside handle().

    use Illuminate\Console\Command;
    use Spatie\Multitenancy\Commands\Concerns\TenantAware;
    
    class YourFavoriteCommand extends Command
    {
        use TenantAware;
    
        protected $signature = 'your-favorite-command {--tenant=*}';
    
        protected int $counter = 0;
    
        public function handle()
        {
            // Reset state at the beginning of each tenant execution
            $this->counter = 0;
    
            $this->incrementCounter();
    
            return $this->line('Counter: '. $this->counter);
        }
    
        public function incrementCounter()
        {
            $this->counter++;
        }
    }
  4. Core concepts of laravel-multitenancy

    main

    The laravel-multitenancy package provides the essential building blocks to make a Laravel application tenant-aware. Its primary responsibilities are:

    1. Tenant Identification: Determining which tenant should be the 'current tenant' for a given incoming request.
    2. Tenant Activation: Defining and executing the logic that occurs when a tenant is made current (e.g., switching database connections).

    The package is designed to support multitenancy strategies involving one or multiple databases and provides built-in support for common requirements like making queued jobs tenant-aware and running Artisan commands across all tenants.

  5. What are Switch Tenant Tasks and how to use them

    main

    Switch tasks are executed every time a tenant is made current or forgotten. They are used to mutate the environment (e.g., switching databases, prefixing cache, or switching route caches).

    Register them in config/multitenancy.php under switch_tenant_tasks.

    Built-in Tasks:

    • SwitchTenantDatabaseTask: Sets the tenant connection's database to the current tenant's database.
    • PrefixCacheTask: Overrides cache.prefix to tenant_{$tenant->id}.
    • SwitchRouteCacheTask: Switches APP_ROUTES_CACHE to a per-tenant file.

    To create a custom task, implement SwitchTenantTask and define makeCurrent(IsTenant $tenant) and forgetCurrent().

    // Registering tasks in config/multitenancy.php
    'switch_tenant_tasks' => [
        \Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask::class,
        // \Spatie\Multitenancy\Tasks\PrefixCacheTask::class,
    ],
    
    // Custom task implementation
    use Spatie\Multitenancy\Contracts\IsTenant;
    use Spatie\Multitenancy\Tasks\SwitchTenantTask;
    
    class SwitchStorageDiskTask implements SwitchTenantTask
    {
        public function makeCurrent(IsTenant $tenant): void
        {
            config(['filesystems.disks.s3.bucket' => $tenant->bucket]);
        }
    
        public function forgetCurrent(): void
        {
            config(['filesystems.disks.s3.bucket' => config('filesystems.default_bucket')]);
        }
    }
  6. Make specific jobs tenant aware

    main

    If queues_are_tenant_aware_by_default is set to false, you can opt-in specific jobs to be tenant aware using one of two methods:

    1. Implement the marker interface: Add Spatie\Multitenancy\Jobs\TenantAware to your job class.
    2. Configuration: Add the job class name to the tenant_aware_jobs array in config/multitenancy.php.
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Spatie\Multitenancy\Jobs\TenantAware;
    
    class TestJob implements ShouldQueue, TenantAware
    {
        public function handle()
        {
            // do the work
        }
    }
  7. Cache separate routes for each tenant

    main

    When every tenant has a unique set of routes, the package generates individual cache files following the pattern: bootstrap/cache/routes-v7-tenant-{$tenant->id}.php.

    Important: Do not use the standard Laravel php artisan route:cache command. Instead, use the tenant-aware artisan wrapper to ensure the correct cache file is generated for the specific tenant:

    php artisan tenant:artisan route:cache
  8. Migrate and seed tenant databases

    main

    The package does not create databases automatically; you must create the physical databases in your application logic (e.g., when a Tenant model is created).

    Once databases exist, use the tenants:artisan command to run migrations and seeders across all tenants. This command loops through all tenants, makes each one current, and executes the provided Artisan command.

    Migration paths:

    • Default: database/migrations
    • Custom: database/migrations/tenant (if preferred)