# Multi-Tenant Implementation Guide
## Step-by-Step Instructions for Laravel Projects

This guide explains how to implement a **domain-based multi-tenant architecture** using a **single database with shared schema** approach in a Laravel application.

---

## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Architecture Overview](#architecture-overview)
3. [Step 1: Database Setup](#step-1-database-setup)
4. [Step 2: Create Tenant (Company) Model](#step-2-create-tenant-company-model)
5. [Step 3: Create Tenant Service Class](#step-3-create-tenant-service-class)
6. [Step 4: Override Database Manager](#step-4-override-database-manager)
7. [Step 5: Create Domain Validation Middleware](#step-5-create-domain-validation-middleware)
8. [Step 6: Modify Eloquent Models](#step-6-modify-eloquent-models)
9. [Step 7: Register Service Provider](#step-7-register-service-provider)
10. [Step 8: Setup Authentication Guards](#step-8-setup-authentication-guards)
11. [Step 9: Create Helper Functions](#step-9-create-helper-functions)
12. [Step 10: Testing](#step-10-testing)
13. [Best Practices](#best-practices)
14. [Common Pitfalls](#common-pitfalls)

---

## Prerequisites

- Laravel 8.x or higher
- PHP 7.4 or higher
- MySQL/PostgreSQL database
- Basic understanding of Laravel Eloquent, Service Providers, and Middleware
- Multiple domains/subdomains pointing to your application

---

## Architecture Overview

**Pattern**: Single Database, Shared Schema with Tenant Discriminator Column

**Key Concepts**:
- Each tenant is identified by a unique domain/subdomain
- All tables (except shared ones) have a `tenant_id` column
- Automatic query scoping at multiple levels
- Super admin portal for managing tenants
- Complete data isolation between tenants

**Data Flow**:
```
Request (tenant1.com) → Middleware → Identify Tenant → Auto-filter all queries by tenant_id → Response
```

---

## Step 1: Database Setup

### 1.1 Create Tenants Table

Create a migration for the tenants table:

```bash
php artisan make:migration create_tenants_table
```

**Migration file**:

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTenantsTable extends Migration
{
    public function up()
    {
        Schema::create('tenants', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('code')->unique();
            $table->string('email')->unique();
            $table->string('domain')->unique()->nullable();
            $table->string('cname')->unique()->nullable(); // Alternative domain
            $table->text('description')->nullable();
            $table->string('logo')->nullable();
            $table->boolean('is_active')->default(1);
            $table->json('settings')->nullable();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('tenants');
    }
}
```

### 1.2 Add tenant_id to Existing Tables

Create a migration to add tenant_id to all tables that need to be scoped:

```bash
php artisan make:migration add_tenant_id_to_tables
```

**Migration file**:

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddTenantIdToTables extends Migration
{
    protected $tables = [
        'users',
        'posts',
        'products',
        'orders',
        'customers',
        // Add all your tenant-specific tables here
    ];

    public function up()
    {
        foreach ($this->tables as $table) {
            Schema::table($table, function (Blueprint $table) {
                $table->unsignedBigInteger('tenant_id')->after('id')->nullable();
                $table->foreign('tenant_id')
                      ->references('id')
                      ->on('tenants')
                      ->onDelete('cascade');
                
                // Add index for performance
                $table->index('tenant_id');
            });
        }
    }

    public function down()
    {
        foreach ($this->tables as $table) {
            Schema::table($table, function (Blueprint $table) {
                $table->dropForeign(['tenant_id']);
                $table->dropColumn('tenant_id');
            });
        }
    }
}
```

### 1.3 Define Shared Tables

Some tables should NOT be scoped by tenant (e.g., countries, currencies). Document these:

```php
// In your config or constant file
protected $sharedTables = [
    'countries',
    'states',
    'currencies',
    'timezones',
    'tenants',
    // Add other shared tables
];
```

---

## Step 2: Create Tenant (Company) Model

Create the Tenant model:

```bash
php artisan make:model Tenant
```

**app/Models/Tenant.php**:

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Tenant extends Model
{
    protected $fillable = [
        'name',
        'code',
        'email',
        'domain',
        'cname',
        'description',
        'logo',
        'is_active',
        'settings',
    ];

    protected $casts = [
        'is_active' => 'boolean',
        'settings' => 'array',
    ];

    /**
     * Get all users for this tenant
     */
    public function users(): HasMany
    {
        return $this->hasMany(User::class);
    }

    /**
     * Scope to get only active tenants
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', 1);
    }

    /**
     * Check if tenant is active
     */
    public function isActive(): bool
    {
        return $this->is_active;
    }
}
```

---

## Step 3: Create Tenant Service Class

Create a service class to handle tenant identification and management:

```bash
mkdir -p app/Services/Tenant
```

**app/Services/Tenant/TenantManager.php**:

```php
<?php

namespace App\Services\Tenant;

use App\Models\Tenant;
use Illuminate\Support\Facades\Cache;

class TenantManager
{
    /**
     * Current tenant instance
     */
    protected static $currentTenant;

    /**
     * Tenant repository
     */
    protected $tenantRepository;

    public function __construct()
    {
        $this->tenantRepository = app(Tenant::class);
    }

    /**
     * Get current tenant based on domain
     * 
     * @param string|null $domain
     * @return Tenant|string|null
     */
    public function getCurrent($domain = null)
    {
        // Return cached tenant if already resolved
        if (static::$currentTenant && !$domain) {
            return static::$currentTenant;
        }

        $primaryDomain = $this->getPrimaryDomain();
        $currentDomain = $domain ?? $this->getCurrentDomain();

        // If accessing primary domain, return super-admin context
        if ($currentDomain === $primaryDomain) {
            static::$currentTenant = 'super-admin';
            return static::$currentTenant;
        }

        // Try to find tenant by domain
        $tenant = Cache::remember("tenant.{$currentDomain}", 3600, function () use ($currentDomain) {
            return Tenant::where('domain', $currentDomain)
                        ->orWhere('cname', $currentDomain)
                        ->active()
                        ->first();
        });

        if (!$tenant) {
            throw new \Exception('Tenant not found for domain: ' . $currentDomain);
        }

        if (!$tenant->isActive()) {
            throw new \Exception('Tenant is not active');
        }

        static::$currentTenant = $tenant;
        return static::$currentTenant;
    }

    /**
     * Set current tenant manually (useful for testing)
     */
    public function setCurrentTenant(Tenant $tenant)
    {
        static::$currentTenant = $tenant;
    }

    /**
     * Check if current domain is the primary domain
     */
    public function isPrimaryDomain(): bool
    {
        return $this->getCurrentDomain() === $this->getPrimaryDomain();
    }

    /**
     * Get the primary domain from config
     */
    protected function getPrimaryDomain(): string
    {
        $url = config('app.url');
        $parsed = parse_url($url);
        return $parsed['host'] ?? 'localhost';
    }

    /**
     * Get current request domain
     */
    protected function getCurrentDomain(): string
    {
        if (app()->runningInConsole()) {
            return $this->getPrimaryDomain();
        }

        $host = request()->getHost();
        
        // Remove port if present
        if (strpos($host, ':') !== false) {
            $host = explode(':', $host)[0];
        }

        return $host;
    }

    /**
     * Get current tenant ID
     */
    public function getCurrentTenantId(): ?int
    {
        $tenant = $this->getCurrent();
        
        if (is_string($tenant)) {
            return null; // Super admin context
        }

        return $tenant?->id;
    }

    /**
     * Clear cached tenant
     */
    public function clearCache($domain = null)
    {
        $domain = $domain ?? $this->getCurrentDomain();
        Cache::forget("tenant.{$domain}");
        static::$currentTenant = null;
    }

    /**
     * Get all tenants
     */
    public function getAllTenants()
    {
        return Tenant::active()->get();
    }
}
```

---

## Step 4: Override Database Manager

Create a custom Database Manager to automatically filter queries:

**app/Database/TenantDatabaseManager.php**:

```php
<?php

namespace App\Database;

use Illuminate\Database\DatabaseManager as BaseDatabaseManager;
use App\Services\Tenant\TenantManager;

class TenantDatabaseManager extends BaseDatabaseManager
{
    /**
     * Tables that should NOT be scoped by tenant
     */
    protected $sharedTables = [
        'tenants',
        'countries',
        'states',
        'currencies',
        'timezones',
        'migrations',
        'failed_jobs',
        'password_resets',
    ];

    /**
     * Begin a fluent query against a database table.
     *
     * @param string $table
     * @return \Illuminate\Database\Query\Builder
     */
    public function table($table, $as = null)
    {
        // Check if user is super admin (bypass tenant scoping)
        if ($this->isSuperAdmin()) {
            return $this->query()->from($table, $as);
        }

        // Parse table name and alias
        $tableName = $this->parseTableName($table);

        // Check if table is in shared tables list
        if ($this->isSharedTable($tableName)) {
            return $this->query()->from($table, $as);
        }

        // Get current tenant
        $tenantManager = app(TenantManager::class);
        $tenantId = $tenantManager->getCurrentTenantId();

        // If no tenant ID, return unscoped query (console commands, etc.)
        if (!$tenantId) {
            return $this->query()->from($table, $as);
        }

        // Apply tenant scope
        $query = $this->query()->from($table, $as);
        
        // Determine column name for where clause
        $columnPrefix = $as ?? $tableName;
        
        return $query->where("{$columnPrefix}.tenant_id", $tenantId);
    }

    /**
     * Parse table name from "table as alias" format
     */
    protected function parseTableName($table): string
    {
        if (strpos($table, ' as ') !== false) {
            return trim(explode(' as ', $table)[0]);
        }
        
        return trim($table);
    }

    /**
     * Check if table should be shared across tenants
     */
    protected function isSharedTable(string $table): bool
    {
        return in_array($table, $this->sharedTables);
    }

    /**
     * Check if current user is super admin
     */
    protected function isSuperAdmin(): bool
    {
        // Implement your super admin check logic
        return auth()->guard('super-admin')->check();
    }
}
```

---

## Step 5: Create Domain Validation Middleware

Create middleware to validate and identify tenant:

```bash
php artisan make:middleware TenantMiddleware
```

**app/Http/Middleware/TenantMiddleware.php**:

```php
<?php

namespace App\Http\Middleware;

use Closure;
use App\Services\Tenant\TenantManager;
use Illuminate\Http\Request;

class TenantMiddleware
{
    protected $tenantManager;

    public function __construct(TenantManager $tenantManager)
    {
        $this->tenantManager = $tenantManager;
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        try {
            // Identify current tenant
            $tenant = $this->tenantManager->getCurrent();

            // If primary domain and not accessing admin routes, redirect
            if ($tenant === 'super-admin' && !$this->isAdminRoute($request)) {
                return redirect()->route('admin.login');
            }

            // If tenant found, continue
            if ($tenant instanceof \App\Models\Tenant) {
                // Optionally set tenant in config or view share
                config(['app.current_tenant' => $tenant]);
                view()->share('currentTenant', $tenant);
            }

            return $next($request);

        } catch (\Exception $e) {
            // Handle tenant not found
            if (request()->expectsJson()) {
                return response()->json([
                    'error' => 'Tenant not found',
                    'message' => $e->getMessage()
                ], 404);
            }

            return response()->view('errors.tenant-not-found', [
                'message' => $e->getMessage()
            ], 404);
        }
    }

    /**
     * Check if request is for admin routes
     */
    protected function isAdminRoute(Request $request): bool
    {
        return $request->is('admin/*') || 
               $request->is('super-admin/*') ||
               $request->is('tenant/register');
    }
}
```

**Register the middleware in `app/Http/Kernel.php`**:

```php
protected $middlewareGroups = [
    'web' => [
        // ... other middleware
        \App\Http\Middleware\TenantMiddleware::class,
    ],
];

protected $routeMiddleware = [
    // ... other middleware
    'tenant' => \App\Http\Middleware\TenantMiddleware::class,
];
```

---

## Step 6: Modify Eloquent Models

### 6.1 Create Base Tenant Model

Create a base model that all tenant-scoped models will extend:

**app/Models/TenantScoped.php**:

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use App\Services\Tenant\TenantManager;

abstract class TenantScoped extends Model
{
    /**
     * Boot the trait
     */
    protected static function boot()
    {
        parent::boot();

        // Automatically set tenant_id on creation
        static::creating(function ($model) {
            if (!$model->tenant_id) {
                $tenantManager = app(TenantManager::class);
                $model->tenant_id = $tenantManager->getCurrentTenantId();
            }
        });

        // Global scope to filter by tenant
        static::addGlobalScope('tenant', function (Builder $builder) {
            $tenantManager = app(TenantManager::class);
            $tenantId = $tenantManager->getCurrentTenantId();

            if ($tenantId && !auth()->guard('super-admin')->check()) {
                $builder->where($builder->getModel()->getTable() . '.tenant_id', $tenantId);
            }
        });
    }

    /**
     * Override newEloquentBuilder for additional scoping
     */
    public function newEloquentBuilder($query)
    {
        $tenantManager = app(TenantManager::class);
        $tenantId = $tenantManager->getCurrentTenantId();

        // If super admin or no tenant, return unscoped builder
        if (auth()->guard('super-admin')->check() || !$tenantId) {
            return new Builder($query);
        }

        // Apply tenant scope
        return new Builder(
            $query->where($this->getTable() . '.tenant_id', $tenantId)
        );
    }

    /**
     * Relationship to tenant
     */
    public function tenant()
    {
        return $this->belongsTo(Tenant::class);
    }
}
```

### 6.2 Update Your Models

Update all models that should be tenant-scoped:

**Example: app/Models/Post.php**:

```php
<?php

namespace App\Models;

class Post extends TenantScoped
{
    protected $fillable = [
        'title',
        'content',
        'tenant_id',
        // other fields
    ];

    // Your model relationships and methods
}
```

**Example: app/Models/User.php**:

```php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    // Add tenant_id to fillable
    protected $fillable = [
        'name',
        'email',
        'password',
        'tenant_id',
    ];

    /**
     * Override newEloquentBuilder for tenant scoping
     */
    public function newEloquentBuilder($query)
    {
        $tenantManager = app(\App\Services\Tenant\TenantManager::class);
        $tenantId = $tenantManager->getCurrentTenantId();

        if (auth()->guard('super-admin')->check() || !$tenantId) {
            return new \Illuminate\Database\Eloquent\Builder($query);
        }

        return new \Illuminate\Database\Eloquent\Builder(
            $query->where('users.tenant_id', $tenantId)
        );
    }

    /**
     * Relationship to tenant
     */
    public function tenant()
    {
        return $this->belongsTo(Tenant::class);
    }
}
```

---

## Step 7: Register Service Provider

Create a service provider to register all tenant services:

```bash
php artisan make:provider TenantServiceProvider
```

**app/Providers/TenantServiceProvider.php**:

```php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\Tenant\TenantManager;
use App\Database\TenantDatabaseManager;

class TenantServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        // Register TenantManager as singleton
        $this->app->singleton(TenantManager::class, function ($app) {
            return new TenantManager();
        });

        // Override default Database Manager
        $this->app->singleton('db', function ($app) {
            return new TenantDatabaseManager($app, $app['db.factory']);
        });

        // Register helper facade
        $this->app->singleton('tenant', function ($app) {
            return $app->make(TenantManager::class);
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        // Register any boot logic here
    }
}
```

**Register in `config/app.php`**:

```php
'providers' => [
    // ... other providers
    App\Providers\TenantServiceProvider::class,
],

'aliases' => [
    // ... other aliases
    'Tenant' => App\Facades\Tenant::class,
],
```

**Create Facade (optional) - app/Facades/Tenant.php**:

```php
<?php

namespace App\Facades;

use Illuminate\Support\Facades\Facade;

class Tenant extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'tenant';
    }
}
```

---

## Step 8: Setup Authentication Guards

Update `config/auth.php` to add super-admin guard:

```php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],

    // Add super admin guard
    'super-admin' => [
        'driver' => 'session',
        'provider' => 'super-admins',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],

    // Add super admin provider
    'super-admins' => [
        'driver' => 'eloquent',
        'model' => App\Models\SuperAdmin::class,
    ],
],
```

**Create SuperAdmin model**:

```bash
php artisan make:model SuperAdmin
php artisan make:migration create_super_admins_table
```

**Migration**:

```php
Schema::create('super_admins', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});
```

**Model - app/Models/SuperAdmin.php**:

```php
<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class SuperAdmin extends Authenticatable
{
    protected $fillable = ['name', 'email', 'password'];
    protected $hidden = ['password', 'remember_token'];
}
```

---

## Step 9: Create Helper Functions

Create a helpers file for convenient access:

**app/Helpers/tenant_helpers.php**:

```php
<?php

use App\Services\Tenant\TenantManager;
use App\Models\Tenant;

if (!function_exists('tenant')) {
    /**
     * Get tenant manager instance
     */
    function tenant(): TenantManager
    {
        return app(TenantManager::class);
    }
}

if (!function_exists('current_tenant')) {
    /**
     * Get current tenant
     */
    function current_tenant(): ?Tenant
    {
        $tenant = tenant()->getCurrent();
        return $tenant instanceof Tenant ? $tenant : null;
    }
}

if (!function_exists('tenant_id')) {
    /**
     * Get current tenant ID
     */
    function tenant_id(): ?int
    {
        return tenant()->getCurrentTenantId();
    }
}

if (!function_exists('is_super_admin')) {
    /**
     * Check if current context is super admin
     */
    function is_super_admin(): bool
    {
        return auth()->guard('super-admin')->check();
    }
}
```

**Autoload in `composer.json`**:

```json
"autoload": {
    "psr-4": {
        "App\\": "app/"
    },
    "files": [
        "app/Helpers/tenant_helpers.php"
    ]
},
```

Run:
```bash
composer dump-autoload
```

---

## Step 10: Testing

### 10.1 Create Test Tenants

**Create a seeder**:

```bash
php artisan make:seeder TenantSeeder
```

```php
<?php

namespace Database\Seeders;

use App\Models\Tenant;
use Illuminate\Database\Seeder;

class TenantSeeder extends Seeder
{
    public function run()
    {
        Tenant::create([
            'name' => 'Tenant One',
            'code' => 'tenant1',
            'email' => 'admin@tenant1.com',
            'domain' => 'tenant1.test',
            'is_active' => true,
        ]);

        Tenant::create([
            'name' => 'Tenant Two',
            'code' => 'tenant2',
            'email' => 'admin@tenant2.com',
            'domain' => 'tenant2.test',
            'is_active' => true,
        ]);
    }
}
```

### 10.2 Configure Local Domains

Add to your `/etc/hosts` (Mac/Linux) or `C:\Windows\System32\drivers\etc\hosts` (Windows):

```
127.0.0.1 tenant1.test
127.0.0.1 tenant2.test
127.0.0.1 admin.test
```

### 10.3 Test Tenant Isolation

**Create test routes in `routes/web.php`**:

```php
Route::middleware(['tenant'])->group(function () {
    Route::get('/test', function () {
        return [
            'tenant' => current_tenant(),
            'users' => \App\Models\User::all(),
        ];
    });
});
```

Visit:
- `http://tenant1.test/test` - Should show Tenant 1 data
- `http://tenant2.test/test` - Should show Tenant 2 data

### 10.4 Create Feature Tests

```bash
php artisan make:test TenantIsolationTest
```

```php
<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;

class TenantIsolationTest extends TestCase
{
    use RefreshDatabase;

    public function test_tenant_isolation()
    {
        $tenant1 = Tenant::factory()->create(['domain' => 'tenant1.test']);
        $tenant2 = Tenant::factory()->create(['domain' => 'tenant2.test']);

        $user1 = User::factory()->create(['tenant_id' => $tenant1->id]);
        $user2 = User::factory()->create(['tenant_id' => $tenant2->id]);

        // Set current tenant to tenant1
        app(\App\Services\Tenant\TenantManager::class)->setCurrentTenant($tenant1);

        // Should only see tenant1 users
        $this->assertCount(1, User::all());
        $this->assertEquals($user1->id, User::first()->id);
    }
}
```

---

## Best Practices

### 1. **Always Use Exceptions for Tenant Not Found**
```php
if (!$tenant) {
    throw new TenantNotFoundException();
}
```

### 2. **Cache Tenant Resolution**
Use caching to avoid repeated database queries:
```php
Cache::remember("tenant.{$domain}", 3600, function () use ($domain) {
    return Tenant::where('domain', $domain)->first();
});
```

### 3. **Index tenant_id Columns**
Always add indexes for performance:
```php
$table->index('tenant_id');
```

### 4. **Use Database Transactions**
When creating tenant data:
```php
DB::transaction(function () use ($tenantData) {
    $tenant = Tenant::create($tenantData);
    // Create related data
});
```

### 5. **Validate Domain Uniqueness**
```php
'domain' => 'required|unique:tenants,domain',
```

### 6. **Soft Delete Tenants**
Instead of hard deletes:
```php
use Illuminate\Database\Eloquent\SoftDeletes;

class Tenant extends Model
{
    use SoftDeletes;
}
```

### 7. **Queue Jobs with Tenant Context**
```php
dispatch(new ProcessOrder($order))->onConnection('tenant-' . tenant_id());
```

### 8. **Backup Before Tenant Deletion**
Always backup tenant data before deletion.

### 9. **Monitor Tenant Performance**
Track query performance per tenant.

### 10. **Implement Rate Limiting Per Tenant**
```php
RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(60)->by(tenant_id());
});
```

---

## Common Pitfalls

### ❌ **1. Forgetting to Scope Raw Queries**
```php
// BAD
DB::select('SELECT * FROM users');

// GOOD
DB::table('users')->get(); // Automatically scoped
```

### ❌ **2. Not Handling Console Commands**
```php
// In console commands, manually set tenant:
$tenant = Tenant::find($tenantId);
app(TenantManager::class)->setCurrentTenant($tenant);
```

### ❌ **3. Circular Dependencies in Service Provider**
Avoid calling tenant() in service provider boot method.

### ❌ **4. Not Testing Cross-Tenant Data Leakage**
Always write tests to ensure tenants can't access each other's data.

### ❌ **5. Hardcoding tenant_id**
```php
// BAD
$user = User::create(['tenant_id' => 1, ...]);

// GOOD
$user = User::create([...]); // Auto-filled by model
```

### ❌ **6. Not Handling Tenant Migrations**
Consider using a separate migrations table per tenant or marking migration status.

### ❌ **7. Session Bleeding Between Tenants**
Use different session names per tenant:
```php
config(['session.cookie' => 'session_' . tenant_id()]);
```

### ❌ **8. Not Validating Super Admin Access**
Always check permissions before allowing tenant data access.

---

## Additional Features to Consider

### 1. **Tenant Onboarding**
Create a registration flow for new tenants.

### 2. **Tenant Settings**
Store tenant-specific configurations.

### 3. **Billing Integration**
Track usage and billing per tenant.

### 4. **Tenant Analytics Dashboard**
Super admin dashboard to monitor all tenants.

### 5. **Domain Management**
Allow tenants to add custom domains.

### 6. **Data Export**
Provide tenant data export functionality.

### 7. **Multi-Tenancy in Queues**
Ensure queued jobs maintain tenant context:

```php
class TenantAwareJob implements ShouldQueue
{
    public $tenantId;

    public function __construct()
    {
        $this->tenantId = tenant_id();
    }

    public function handle()
    {
        $tenant = Tenant::find($this->tenantId);
        app(TenantManager::class)->setCurrentTenant($tenant);
        
        // Your job logic
    }
}
```

### 8. **Subdomain Support**
Automatically create subdomains for new tenants.

### 9. **Tenant Impersonation**
Allow super admin to impersonate tenants for support.

### 10. **Audit Logging**
Track all actions per tenant for compliance.

---

## Deployment Checklist

- [ ] All migrations run successfully
- [ ] tenant_id indexed on all tables
- [ ] Middleware registered in Kernel
- [ ] Service provider registered
- [ ] Helper functions autoloaded
- [ ] Tests passing (especially isolation tests)
- [ ] Domain DNS configured
- [ ] SSL certificates for all domains
- [ ] Cache configured correctly
- [ ] Queue workers configured
- [ ] Monitoring setup for tenant performance
- [ ] Backup strategy in place
- [ ] Documentation updated

---

## Troubleshooting

### Issue: "Tenant not found" errors
**Solution**: Check domain configuration and DNS settings.

### Issue: Data bleeding between tenants
**Solution**: Verify all models extend TenantScoped and queries use query builder.

### Issue: Performance degradation
**Solution**: Add indexes on tenant_id columns and implement caching.

### Issue: Console commands not working
**Solution**: Manually set tenant context in console commands.

### Issue: Tests failing
**Solution**: Use RefreshDatabase trait and seed test tenants.

---

## Conclusion

This multi-tenant architecture provides:
✅ Strong data isolation
✅ Scalable single-database approach
✅ Easy tenant management
✅ Performance optimization
✅ Developer-friendly API

For questions or contributions, please refer to your project's documentation.

---

**Version**: 1.0  
**Last Updated**: 2025  
**Laravel Version**: 8.x+  
**License**: MIT

