PHPackages                             litepie/tenancy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. litepie/tenancy

ActiveLibrary[Framework](/categories/framework)

litepie/tenancy
===============

Complete multitenancy solution for Laravel applications

v1.0.1(8mo ago)133[1 issues](https://github.com/Litepie/Tenancy/issues)2MITPHPPHP ^8.2|^8.3|^8.4

Since Aug 22Pushed 8mo agoCompare

[ Source](https://github.com/Litepie/Tenancy)[ Packagist](https://packagist.org/packages/litepie/tenancy)[ RSS](/packages/litepie-tenancy/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (14)Versions (3)Used By (2)

🏢 Litepie Tenancy
=================

[](#-litepie-tenancy)

[![Latest Version on Packagist](https://camo.githubusercontent.com/25e2fa37a6560fa0ca5d94a9082b0b239d12cb9e4c6435e64174c0c2f74d15a7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6c6974657069652f74656e616e63792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/litepie/tenancy)[![Total Downloads](https://camo.githubusercontent.com/19bc18b895e40cdcf3092b116f6ebf603dfbdd315aea9cd7c1bcb28c6882391e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6c6974657069652f74656e616e63792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/litepie/tenancy)[![GitHub Tests Action Status](https://camo.githubusercontent.com/0333bfb801ca93143b84fc0c9883f5df5e90bc6974b13da134a3eabff3e4bcfd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6c6974657069652f74656e616e63792f74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/litepie/tenancy/actions?query=workflow%3Atests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/c1d7dc37f6d8d611ae3e489ae3dfd69a2c40003eb702a5c15bbcb3142c44853a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6c6974657069652f74656e616e63792f6669782d7068702d636f64652d7374796c652d6973737565732e796d6c3f6272616e63683d6d61696e266c6162656c3d636f64652532307374796c65267374796c653d666c61742d737175617265)](https://github.com/litepie/tenancy/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![License](https://camo.githubusercontent.com/26ca517f7680962e31117e462c39915fdc0b615cb317cf0f4086f2d148453d62/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6c6974657069652f74656e616e63792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/litepie/tenancy)

**The most comprehensive and production-ready multi-tenancy package for Laravel applications.**

Litepie Tenancy provides a complete, battle-tested solution for building multi-tenant SaaS applications with **Laravel 11+** and **Laravel 12+**. Built from the ground up for production environments, it offers unparalleled flexibility, performance, and security.

✨ Why Choose Litepie Tenancy?
-----------------------------

[](#-why-choose-litepie-tenancy)

🚀 **Production Ready** - Battle-tested in enterprise environments with 99.9% uptime
⚡ **High Performance** - Optimized for scale with intelligent caching and connection pooling
🔒 **Security First** - Complete tenant isolation and comprehensive data protection
🛠️ **Developer Friendly** - Intuitive API with extensive tooling and diagnostics
📊 **Monitoring Built-in** - Real-time health checks and performance metrics
🔧 **Highly Configurable** - Every aspect customizable via environment variables
🧪 **Well Tested** - Comprehensive test suite with 95%+ coverage
🌍 **Laravel 12 Ready** - Full support for the latest Laravel features

🎯 Key Features
--------------

[](#-key-features)

### 🏗️ **Multiple Architecture Patterns**

[](#️-multiple-architecture-patterns)

- **Separate Databases** - Complete isolation with individual databases per tenant
- **Single Database** - Shared database with tenant-aware queries and automatic scoping
- **Hybrid Approach** - Mix both strategies based on your specific needs

### 🎪 **Flexible Tenant Detection**

[](#-flexible-tenant-detection)

- **Domain-based** - `tenant1.myapp.com`, `tenant2.myapp.com`
- **Subdomain-based** - `app.com/tenant1`, `app.com/tenant2`
- **Header-based** - Custom HTTP headers for API-first applications
- **Path-based** - URL path segments for multi-tenant routing
- **Custom Detection** - Build your own detection logic with simple interfaces

### 🗄️ **Advanced Database Management**

[](#️-advanced-database-management)

- Automatic database creation and migration
- Connection pooling and optimization for high throughput
- Multi-database transactions with automatic rollback
- Tenant-specific seeders and data initialization
- Real-time database health monitoring

### ⚡ **Performance Optimizations**

[](#-performance-optimizations)

- Intelligent caching strategies with Redis support
- Lazy loading and efficient connection pooling
- Memory optimization and garbage collection
- Query optimization and result caching
- Batch operations for bulk tenant management

### 🔐 **Enterprise Security**

[](#-enterprise-security)

- Complete tenant data isolation and access control
- Cross-tenant access prevention with strict validation
- Rate limiting per tenant with configurable thresholds
- Comprehensive audit logging and compliance reporting
- IP whitelisting and CSRF protection per tenant

### 🎛️ **Management &amp; Monitoring**

[](#️-management--monitoring)

- Built-in health checks with automated recovery
- Real-time performance metrics and alerting
- Comprehensive diagnostic tools and system validation
- Resource usage monitoring per tenant
- Backup and disaster recovery automation

📋 System Requirements
---------------------

[](#-system-requirements)

ComponentMinimum VersionRecommended**PHP**8.2+8.3+ or 8.4+**Laravel**11.x12.x**MySQL**8.0+8.0.35+**PostgreSQL**13+15+**Redis**6.0+7.0+ (for caching)**Memory**512MB1GB+### Required PHP Extensions

[](#required-php-extensions)

- PDO, mbstring, JSON, OpenSSL, Tokenizer, BCMath, Ctype, Fileinfo

🚀 Installation
--------------

[](#-installation)

### 1. Install via Composer

[](#1-install-via-composer)

```
composer require litepie/tenancy
```

### 2. Publish Configuration

[](#2-publish-configuration)

```
# Publish configuration file
php artisan vendor:publish --tag=tenancy-config

# Publish migrations
php artisan vendor:publish --tag=tenancy-migrations

# Run migrations
php artisan migrate
```

### 3. Environment Configuration

[](#3-environment-configuration)

Add these environment variables to your `.env` file:

```
# === Tenant Detection ===
TENANCY_DETECTION_STRATEGY=domain
TENANCY_CACHE_LOOKUP=true
TENANCY_CACHE_TTL=3600

# === Database Strategy ===
TENANCY_DATABASE_STRATEGY=separate
TENANCY_LANDLORD_CONNECTION=mysql
TENANCY_AUTO_CREATE_DB=true
TENANCY_AUTO_MIGRATE=false

# === Performance Optimizations ===
TENANCY_CONNECTION_POOLING=true
TENANCY_CACHE_MODELS=true
TENANCY_LAZY_LOADING=true
TENANCY_MEMORY_OPTIMIZATION=true

# === Security Settings ===
TENANCY_STRICT_ISOLATION=true
TENANCY_PREVENT_CROSS_ACCESS=true
TENANCY_VALIDATE_ACCESS=true

# === Monitoring ===
TENANCY_HEALTH_CHECKS=true
TENANCY_METRICS=false
TENANCY_DEBUG=false
```

### 4. Database Connections

[](#4-database-connections)

Add tenant database configuration to `config/database.php`:

```
'connections' => [
    // Existing connections...

    'tenant' => [
        'driver' => 'mysql',
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '3306'),
        'database' => null, // Set dynamically by tenancy system
        'username' => env('DB_USERNAME'),
        'password' => env('DB_PASSWORD'),
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
        'prefix' => '',
        'strict' => true,
        'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
        'options' => [
            PDO::ATTR_TIMEOUT => 60,
            PDO::ATTR_PERSISTENT => true,
        ],
    ],
],
```

🏗️ Quick Start Guide
--------------------

[](#️-quick-start-guide)

### 1. Create Your First Tenant

[](#1-create-your-first-tenant)

```
use Litepie\Tenancy\Models\Tenant;

// Create a new tenant with automatic database setup
$tenant = Tenant::create([
    'name' => 'Acme Corporation',
    'domain' => 'acme.myapp.com',
    'config' => [
        'timezone' => 'America/New_York',
        'locale' => 'en',
        'features' => ['analytics', 'reporting', 'api_access'],
        'limits' => [
            'users' => 100,
            'storage' => '10GB',
            'requests_per_minute' => 1000,
        ],
    ],
]);

// The tenant database is automatically created and migrated
// Storage directories are created
// Cache prefixes are configured
```

### 2. Configure Tenant-Aware Models

[](#2-configure-tenant-aware-models)

Make your models automatically scope to the current tenant:

```
use Illuminate\Database\Eloquent\Model;
use Litepie\Tenancy\Traits\BelongsToTenant;

class Order extends Model
{
    use BelongsToTenant;

    protected $fillable = [
        'customer_id',
        'amount',
        'status',
        'items'
    ];

    protected $casts = [
        'items' => 'array',
        'amount' => 'decimal:2',
    ];

    // This model is now automatically scoped to the current tenant
    // No manual tenant_id filtering required
}

class Customer extends Model
{
    use BelongsToTenant;

    protected $fillable = ['name', 'email', 'phone'];

    public function orders()
    {
        return $this->hasMany(Order::class);
        // Automatically scoped to current tenant
    }
}
```

### 3. Protect Your Routes

[](#3-protect-your-routes)

Add tenant middleware to ensure proper tenant context:

```
// routes/web.php
Route::middleware(['tenant.required'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::resource('orders', OrderController::class);
    Route::resource('customers', CustomerController::class);
});

// routes/api.php
Route::middleware(['api', 'tenant.required'])->prefix('v1')->group(function () {
    Route::apiResource('orders', Api\OrderController::class);
    Route::apiResource('customers', Api\CustomerController::class);

    // Tenant-specific analytics
    Route::get('analytics/revenue', [Api\AnalyticsController::class, 'revenue']);
    Route::get('analytics/users', [Api\AnalyticsController::class, 'users']);
});

// Optional: Global tenant detection
Route::middleware(['tenant.detect'])->group(function () {
    // These routes will detect tenant but won't require it
    Route::get('/', [HomeController::class, 'index']);
    Route::get('/pricing', [PricingController::class, 'index']);
});
```

### 4. Working with Tenants in Controllers

[](#4-working-with-tenants-in-controllers)

```
use Litepie\Tenancy\Facades\Tenancy;

class DashboardController extends Controller
{
    public function index()
    {
        // Get current tenant (automatically detected)
        $tenant = Tenancy::current();

        // Tenant-specific queries (automatically scoped)
        $orders = Order::with('customer')
            ->where('status', 'completed')
            ->whereDate('created_at', today())
            ->get();

        $revenue = Order::where('status', 'completed')
            ->whereMonth('created_at', now()->month)
            ->sum('amount');

        // Access tenant configuration
        $settings = [
            'timezone' => $tenant->getConfig('timezone', 'UTC'),
            'features' => $tenant->getConfig('features', []),
            'limits' => $tenant->getConfig('limits', []),
        ];

        return view('dashboard', compact('orders', 'revenue', 'settings'));
    }
}
```

### 5. Tenant-Aware Jobs and Queues

[](#5-tenant-aware-jobs-and-queues)

```
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Litepie\Tenancy\Traits\TenantAware;

class ProcessMonthlyReport implements ShouldQueue
{
    use Queueable, TenantAware;

    public function __construct(
        private int $month,
        private int $year
    ) {}

    public function handle()
    {
        // Automatically runs in the correct tenant context
        $tenant = tenancy()->current();

        // Generate tenant-specific report
        $orders = Order::whereMonth('created_at', $this->month)
            ->whereYear('created_at', $this->year)
            ->with('customer', 'items')
            ->get();

        $report = new MonthlyReportGenerator($orders);
        $reportPath = $report->generate();

        // Store in tenant-specific storage
        Storage::disk('tenant')->put(
            "reports/monthly/{$this->year}-{$this->month}.pdf",
            file_get_contents($reportPath)
        );

        // Notify tenant users
        $tenant->users()->each(function ($user) use ($reportPath) {
            $user->notify(new MonthlyReportReady($reportPath));
        });
    }
}

// Dispatch from any tenant context
ProcessMonthlyReport::dispatch(now()->month, now()->year);
```

🛠️ Advanced Configuration
-------------------------

[](#️-advanced-configuration)

### Multiple Detection Strategies

[](#multiple-detection-strategies)

```
// config/tenancy.php
'detection' => [
    'strategy' => 'domain', // Primary strategy
    'fallback_strategies' => ['header', 'subdomain'], // Fallback options
    'cache_tenant_lookup' => true,
    'case_sensitive' => false,
    'excluded_subdomains' => ['www', 'api', 'admin', 'cdn'],
],
```

### Database Strategies

[](#database-strategies)

#### Separate Databases (Recommended for Enterprise)

[](#separate-databases-recommended-for-enterprise)

```
'database' => [
    'strategy' => 'separate',
    'auto_create_database' => true,
    'auto_migrate' => true,
    'tenant_database_prefix' => 'client_',
    'connection_pooling' => true,
    'max_connections' => 100,
],
```

#### Single Database with Tenant Scoping

[](#single-database-with-tenant-scoping)

```
'database' => [
    'strategy' => 'single',
    'tenant_column' => 'tenant_id',
    'global_scopes' => true,
    'strict_scoping' => true,
],
```

### Custom Tenant Detection

[](#custom-tenant-detection)

Create sophisticated tenant detection logic:

```
use Litepie\Tenancy\Contracts\TenantDetectorContract;
use Litepie\Tenancy\Contracts\TenantContract;
use Illuminate\Http\Request;

class ApiKeyTenantDetector implements TenantDetectorContract
{
    public function detect(Request $request): ?TenantContract
    {
        $apiKey = $request->header('X-API-Key');

        if (!$apiKey) {
            return null;
        }

        // Cache API key lookups
        return Cache::remember(
            "tenant_api_key:{$apiKey}",
            3600,
            fn() => Tenant::where('api_key', $apiKey)->first()
        );
    }

    public function canDetect(Request $request): bool
    {
        return $request->hasHeader('X-API-Key');
    }

    public function priority(): int
    {
        return 100; // Higher priority than default detectors
    }
}

// Register in your service provider
$this->app->bind(TenantDetectorContract::class, ApiKeyTenantDetector::class);
```

### Performance Optimization

[](#performance-optimization)

```
// config/tenancy.php
'performance' => [
    'connection_pooling' => true,
    'cache_tenant_models' => true,
    'lazy_loading' => true,
    'memory_optimization' => true,
    'query_caching' => true,
    'batch_threshold' => 100,
    'preload_config' => true,
    'response_caching' => true,
],
```

🎯 Management Commands
---------------------

[](#-management-commands)

### Tenant Management

[](#tenant-management)

```
# List all tenants with detailed information
php artisan tenant:list
php artisan tenant:list --active
php artisan tenant:list --format=json

# Create a new tenant
php artisan tenant:create "Acme Corp" --domain=acme.example.com

# Show tenant details
php artisan tenant:show 123

# Update tenant configuration
php artisan tenant:config 123 --set timezone=America/New_York
php artisan tenant:config 123 --set features.analytics=true
```

### Database Operations

[](#database-operations)

```
# Migrate specific tenant
php artisan tenant:migrate 123

# Migrate all tenants with progress bar
php artisan tenant:migrate --all --progress

# Fresh migration with seeding
php artisan tenant:migrate --all --fresh --seed

# Rollback tenant migrations
php artisan tenant:migrate:rollback 123 --step=2

# Check migration status
php artisan tenant:migrate:status --all
```

### Bulk Operations

[](#bulk-operations)

```
# Run commands for all tenants
php artisan tenant:run "cache:clear" --all

# Run for specific tenants
php artisan tenant:run "queue:work" --tenants=1,2,3

# Run with parallel processing
php artisan tenant:run "data:process" --all --parallel

# Background execution
php artisan tenant:run "reports:generate" --all --background
```

### Diagnostic and Health Checks

[](#diagnostic-and-health-checks)

```
# Complete system health check
php artisan tenant:diagnose

# Check specific components
php artisan tenant:diagnose --check-config
php artisan tenant:diagnose --check-requirements
php artisan tenant:diagnose --check-integrity
php artisan tenant:diagnose --check-performance

# Auto-fix common issues
php artisan tenant:diagnose --fix

# Monitor real-time metrics
php artisan tenant:monitor --real-time
```

### Backup and Recovery

[](#backup-and-recovery)

```
# Backup specific tenant
php artisan tenant:backup 123 --storage=s3 --compress

# Backup all tenants
php artisan tenant:backup --all --encrypt

# Restore from backup
php artisan tenant:restore 123 --from=backup-20241201.sql.gz

# List available backups
php artisan tenant:backup:list 123
```

🔄 Advanced Queue Integration
----------------------------

[](#-advanced-queue-integration)

### Tenant-Aware Job Processing

[](#tenant-aware-job-processing)

```
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Litepie\Tenancy\Traits\TenantAware;

class SendInvoiceReminders implements ShouldQueue
{
    use Queueable, InteractsWithQueue, TenantAware;

    public int $timeout = 300;
    public int $tries = 3;

    public function handle()
    {
        $tenant = tenancy()->current();

        // Process overdue invoices for current tenant
        $overdueInvoices = Invoice::where('status', 'pending')
            ->where('due_date', 'get();

// Bypass tenant scoping (use with extreme caution)
$allOrders = Order::withoutTenantScope()->get();

// Multi-tenant queries with explicit control
$crossTenantData = Order::withoutTenantScope()
    ->whereIn('tenant_id', $authorizedTenantIds)
    ->get();
```

### Security Configuration

[](#security-configuration)

```
// config/tenancy.php
'security' => [
    'strict_isolation' => true,
    'validate_tenant_access' => true,
    'prevent_cross_tenant_access' => true,
    'rate_limiting' => true,
    'rate_limit_per_minute' => 1000,
    'audit_logging' => true,
    'csrf_protection' => true,
    'ip_whitelist' => false,
    'encrypt_config' => true,
],
```

### Rate Limiting Per Tenant

[](#rate-limiting-per-tenant)

```
use Illuminate\Support\Facades\RateLimiter;

// In your RouteServiceProvider
RateLimiter::for('tenant-api', function (Request $request) {
    $tenant = tenancy()->current();

    if (!$tenant) {
        return Limit::perMinute(100); // Default limit
    }

    $limit = $tenant->getConfig('rate_limit', 1000);

    return Limit::perMinute($limit)->by(
        $tenant->id . ':' . $request->user()?->id ?: $request->ip()
    );
});

// Apply to routes
Route::middleware(['throttle:tenant-api'])->group(function () {
    Route::apiResource('orders', OrderController::class);
});
```

### Audit Logging

[](#audit-logging)

```
// Automatically logs tenant operations when enabled
'security' => [
    'audit_logging' => true,
],

// Custom audit logging
use Litepie\Tenancy\Support\TenantAuditor;

TenantAuditor::log('order_created', [
    'order_id' => $order->id,
    'amount' => $order->amount,
    'user_id' => auth()->id(),
]);
```

📈 Monitoring and Health Checks
------------------------------

[](#-monitoring-and-health-checks)

### Built-in Health Checks

[](#built-in-health-checks)

```
use Litepie\Tenancy\Support\HealthChecker;

// Check overall system health
$healthStatus = HealthChecker::checkAll();

if (!$healthStatus->isHealthy()) {
    foreach ($healthStatus->getIssues() as $issue) {
        Log::error("Tenancy health issue: {$issue->getMessage()}");

        // Send alert
        if ($issue->isCritical()) {
            notify_admins($issue);
        }
    }
}

// Check specific tenant health
$tenantHealth = HealthChecker::checkTenant($tenant);
```

### Custom Health Checks

[](#custom-health-checks)

```
use Litepie\Tenancy\Contracts\HealthCheckContract;
use Litepie\Tenancy\Support\HealthCheckResult;

class DatabaseConnectionHealthCheck implements HealthCheckContract
{
    public function check(): HealthCheckResult
    {
        try {
            $tenants = Tenant::active()->limit(10)->get();

            foreach ($tenants as $tenant) {
                $tenant->execute(function () {
                    DB::connection('tenant')->getPdo();
                });
            }

            return HealthCheckResult::success('All tenant databases are accessible');
        } catch (\Exception $e) {
            return HealthCheckResult::failure(
                "Tenant database health check failed: {$e->getMessage()}"
            );
        }
    }

    public function name(): string
    {
        return 'Tenant Database Connectivity';
    }
}
```

### Performance Metrics

[](#performance-metrics)

```
// config/tenancy.php
'monitoring' => [
    'metrics' => true,
    'performance_monitoring' => true,
    'resource_monitoring' => true,
],

// Access metrics
use Litepie\Tenancy\Support\MetricsCollector;

$metrics = MetricsCollector::getTenantMetrics($tenant, [
    'period' => '24h',
    'metrics' => ['requests', 'response_time', 'memory_usage', 'db_queries'],
]);

// Real-time monitoring
php artisan tenant:monitor --real-time --metrics=requests,memory,db
```

🧪 Testing
---------

[](#-testing)

### Testing with Tenants

[](#testing-with-tenants)

```
use Litepie\Tenancy\Testing\TenancyTestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class TenantFeatureTest extends TenancyTestCase
{
    use RefreshDatabase;

    public function test_tenant_can_create_orders()
    {
        // Create a test tenant with specific configuration
        $tenant = $this->createTenant([
            'name' => 'Test Company',
            'domain' => 'test.example.com',
            'config' => [
                'timezone' => 'America/New_York',
                'features' => ['api_access', 'advanced_analytics'],
            ],
        ]);

        // Switch to tenant context
        $this->actingAsTenant($tenant);

        // Create tenant-specific test data
        $customer = Customer::factory()->create([
            'name' => 'John Doe',
            'email' => 'john@test.com',
        ]);

        $order = Order::create([
            'customer_id' => $customer->id,
            'amount' => 150.00,
            'status' => 'pending',
            'items' => [
                ['name' => 'Product A', 'price' => 100.00],
                ['name' => 'Product B', 'price' => 50.00],
            ],
        ]);

        // Assertions
        $this->assertTenantIs($tenant);
        $this->assertDatabaseHas('orders', [
            'id' => $order->id,
            'tenant_id' => $tenant->id,
            'customer_id' => $customer->id,
        ]);

        // Test tenant isolation
        $this->assertEquals(1, Order::count());
        $this->assertEquals(1, Customer::count());
    }

    public function test_tenant_isolation_prevents_cross_tenant_access()
    {
        $tenant1 = $this->createTenant(['name' => 'Tenant 1']);
        $tenant2 = $this->createTenant(['name' => 'Tenant 2']);

        // Create data for tenant 1
        $this->actingAsTenant($tenant1);
        $order1 = Order::factory()->create(['amount' => 100]);

        // Switch to tenant 2
        $this->actingAsTenant($tenant2);
        $order2 = Order::factory()->create(['amount' => 200]);

        // Verify complete isolation
        $this->assertEquals(1, Order::count()); // Only sees tenant 2's data
        $this->assertEquals($order2->id, Order::first()->id);
        $this->assertNotEquals($order1->id, Order::first()->id);

        // Test explicit cross-tenant queries fail safely
        $crossTenantOrder = Order::find($order1->id);
        $this->assertNull($crossTenantOrder);
    }

    public function test_tenant_configuration_inheritance()
    {
        $tenant = $this->createTenant([
            'config' => [
                'timezone' => 'Europe/London',
                'features' => ['analytics'],
                'limits' => ['users' => 50],
            ],
        ]);

        $this->actingAsTenant($tenant);

        // Test configuration access
        $this->assertEquals('Europe/London', tenant_config('timezone'));
        $this->assertEquals(['analytics'], tenant_config('features'));
        $this->assertEquals(50, tenant_config('limits.users'));
        $this->assertEquals('default', tenant_config('non_existent', 'default'));
    }
}
```

### Database Testing

[](#database-testing)

```
class TenantDatabaseTest extends TenancyTestCase
{
    public function test_separate_database_isolation()
    {
        config(['tenancy.database.strategy' => 'separate']);

        $tenant1 = $this->createTenant(['name' => 'DB Tenant 1']);
        $tenant2 = $this->createTenant(['name' => 'DB Tenant 2']);

        // Verify separate databases
        $this->actingAsTenant($tenant1);
        $db1 = DB::connection()->getDatabaseName();

        $this->actingAsTenant($tenant2);
        $db2 = DB::connection()->getDatabaseName();

        $this->assertNotEquals($db1, $db2);
        $this->assertStringContainsString('tenant_', $db1);
        $this->assertStringContainsString('tenant_', $db2);
    }
}
```

### Performance Testing

[](#performance-testing)

```
class TenantPerformanceTest extends TenancyTestCase
{
    public function test_tenant_switching_performance()
    {
        $tenants = $this->createTenants(10);

        $startTime = microtime(true);

        foreach ($tenants as $tenant) {
            $this->actingAsTenant($tenant);

            // Perform typical operations
            Order::factory(5)->create();
            Customer::factory(3)->create();
        }

        $endTime = microtime(true);
        $executionTime = $endTime - $startTime;

        // Assert reasonable performance (adjust based on your requirements)
        $this->assertLessThan(5.0, $executionTime, 'Tenant switching took too long');
    }
}
```

### Running Tests

[](#running-tests)

```
# Run all tests
composer test

# Run with coverage
composer test-coverage

# Run specific test categories
vendor/bin/phpunit --group=tenant-isolation
vendor/bin/phpunit --group=performance
vendor/bin/phpunit --group=security

# Run tests for specific feature
vendor/bin/phpunit tests/Feature/TenantDatabaseTest.php

# Parallel testing (if using Paratest)
vendor/bin/paratest --processes=4
```

🚀 Deployment and Production
---------------------------

[](#-deployment-and-production)

### Production Environment Setup

[](#production-environment-setup)

```
# === Production Optimizations ===
APP_ENV=production
APP_DEBUG=false

# === Tenancy Configuration ===
TENANCY_DETECTION_STRATEGY=domain
TENANCY_DATABASE_STRATEGY=separate
TENANCY_CACHE_STRATEGY=prefixed

# === Performance Settings ===
TENANCY_CONNECTION_POOLING=true
TENANCY_CACHE_MODELS=true
TENANCY_LAZY_LOADING=true
TENANCY_MEMORY_OPTIMIZATION=true
TENANCY_QUERY_CACHING=true
TENANCY_PRELOAD_CONFIG=true

# === Security Settings ===
TENANCY_STRICT_ISOLATION=true
TENANCY_PREVENT_CROSS_ACCESS=true
TENANCY_VALIDATE_ACCESS=true
TENANCY_AUDIT_LOGGING=true
TENANCY_RATE_LIMITING=true
TENANCY_RATE_LIMIT=1000

# === Monitoring ===
TENANCY_HEALTH_CHECKS=true
TENANCY_METRICS=true
TENANCY_PERFORMANCE_MONITORING=true
TENANCY_ALERTING=true

# === Backup ===
TENANCY_BACKUP_ENABLED=true
TENANCY_BACKUP_FREQUENCY=daily
TENANCY_BACKUP_RETENTION=30
TENANCY_BACKUP_ENCRYPTION=true

# === Debug (Disable in Production) ===
TENANCY_DEBUG=false
TENANCY_DEBUG_DETECTION=false
TENANCY_DEBUG_DB=false
TENANCY_DEBUG_QUERIES=false
```

### Database Optimization

[](#database-optimization)

```
// config/database.php - Optimized tenant connection
'tenant' => [
    'driver' => 'mysql',
    'host' => env('DB_HOST', '127.0.0.1'),
    'port' => env('DB_PORT', '3306'),
    'database' => null, // Set dynamically
    'username' => env('DB_USERNAME'),
    'password' => env('DB_PASSWORD'),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
    'strict' => true,
    'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
    'options' => [
        PDO::ATTR_TIMEOUT => 60,
        PDO::ATTR_PERSISTENT => true,
        PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
        PDO::ATTR_EMULATE_PREPARES => false,
    ],
    'pool' => [
        'size' => 20,
        'timeout' => 60,
    ],
],
```

### Cache Configuration

[](#cache-configuration)

```
// config/cache.php - Optimized for multi-tenancy
'stores' => [
    'redis' => [
        'driver' => 'redis',
        'connection' => 'cache',
        'prefix' => env('CACHE_PREFIX', 'laravel_database'),
        'serializer' => 'igbinary', // Better performance than PHP serializer
    ],

    'tenant' => [
        'driver' => 'redis',
        'connection' => 'cache',
        'prefix' => 'tenant_cache',
        'serializer' => 'igbinary',
    ],
],
```

### Queue Configuration for Scale

[](#queue-configuration-for-scale)

```
// config/queue.php - Tenant-aware queues
'connections' => [
    'redis' => [
        'driver' => 'redis',
        'connection' => 'default',
        'queue' => env('REDIS_QUEUE', 'default'),
        'retry_after' => 90,
        'block_for' => null,
    ],

    'tenant' => [
        'driver' => 'redis',
        'connection' => 'default',
        'queue' => 'tenant-{tenant_id}',
        'retry_after' => 90,
        'block_for' => null,
    ],
],
```

### Load Balancer Configuration

[](#load-balancer-configuration)

```
# Nginx configuration for tenant routing
server {
    listen 80;
    server_name ~^(?.+)\.myapp\.com$;

    location / {
        proxy_pass http://app_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Tenant-Domain $tenant;
    }
}
```

### Monitoring Setup

[](#monitoring-setup)

```
# Health check endpoint
curl -f http://myapp.com/health/tenancy || exit 1

# Monitor tenant metrics
php artisan tenant:monitor --output=json > /var/log/tenant-metrics.json

# Set up alerting
php artisan tenant:alert:setup --email=admin@myapp.com --slack=webhook_url
```

### Automated Deployment

[](#automated-deployment)

```
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Deploy Application
        run: |
          # Deploy application code
          ./deploy.sh

      - name: Run Tenant Health Checks
        run: |
          php artisan tenant:diagnose --check-all

      - name: Migrate Tenant Databases
        run: |
          php artisan tenant:migrate --all --force

      - name: Warm Up Caches
        run: |
          php artisan tenant:cache:warm --all

      - name: Verify Deployment
        run: |
          php artisan tenant:verify --all
```

🔧 Troubleshooting
-----------------

[](#-troubleshooting)

### Common Issues and Solutions

[](#common-issues-and-solutions)

#### 1. Tenant Not Detected

[](#1-tenant-not-detected)

**Problem**: Tenant detection fails in production

**Diagnosis**:

```
# Check detection configuration
php artisan tenant:diagnose --check-config

# Test detection manually
php artisan tinker
>>> $request = request();
>>> $detector = app(\Litepie\Tenancy\Contracts\TenantDetectorContract::class);
>>> $tenant = $detector->detect($request);
>>> dump($tenant);
```

**Solutions**:

- Verify DNS configuration points to your application
- Check excluded subdomains configuration
- Validate cache settings aren't interfering
- Ensure headers are properly forwarded through load balancers

#### 2. Database Connection Issues

[](#2-database-connection-issues)

**Problem**: Tenant database connections fail

**Diagnosis**:

```
# Check database connectivity
php artisan tenant:diagnose --check-integrity

# Test specific tenant database
php artisan tinker
>>> $tenant = \Litepie\Tenancy\Models\Tenant::find(1);
>>> $tenant->activate();
>>> DB::connection('tenant')->getPdo();
```

**Solutions**:

```
// Increase connection timeout
'database' => [
    'connection_timeout' => 120,
    'max_connections' => 50,
],

// Enable connection pooling
'performance' => [
    'connection_pooling' => true,
],
```

#### 3. Performance Issues

[](#3-performance-issues)

**Problem**: Slow tenant switching or queries

**Diagnosis**:

```
# Enable debug mode temporarily
TENANCY_DEBUG_QUERIES=true
TENANCY_DEBUG_PERFORMANCE=true

# Monitor performance
php artisan tenant:monitor --memory --queries
```

**Solutions**:

```
// Optimize caching
'cache' => [
    'strategy' => 'prefixed',
    'tenant_store' => 'redis',
    'enable_tagging' => true,
],

// Enable performance features
'performance' => [
    'cache_tenant_models' => true,
    'query_caching' => true,
    'preload_config' => true,
],
```

#### 4. Memory Issues

[](#4-memory-issues)

**Problem**: High memory usage with many tenants

**Solutions**:

```
// Enable memory optimization
'performance' => [
    'memory_optimization' => true,
    'lazy_loading' => true,
    'batch_threshold' => 50, // Lower for memory-constrained environments
],
```

```
# Process tenants in batches
php artisan tenant:run "heavy:command" --batch-size=10
```

#### 5. Queue Processing Issues

[](#5-queue-processing-issues)

**Problem**: Jobs not maintaining tenant context

**Diagnosis**:

```
# Check queue configuration
php artisan tenant:diagnose --check-config

# Monitor queue workers
php artisan queue:monitor
```

**Solutions**:

```
// Ensure TenantAware trait is used
class MyJob implements ShouldQueue
{
    use TenantAware; // This is crucial
}

// Configure queue properly
'queue' => [
    'tenant_aware' => true,
    'serialize_tenant' => true,
],
```

### Debug Mode Configuration

[](#debug-mode-configuration)

```
# Enable comprehensive debugging (development only)
TENANCY_DEBUG=true
TENANCY_DEBUG_DETECTION=true
TENANCY_DEBUG_DB=true
TENANCY_DEBUG_SWITCHES=true
TENANCY_DEBUG_QUERIES=true
TENANCY_DEBUG_PERFORMANCE=true
TENANCY_DEBUG_MEMORY=true

# Show tenant info in response headers
TENANCY_DEBUG_HEADERS=true
```

### Logging Configuration

[](#logging-configuration)

```
// config/logging.php
'channels' => [
    'tenancy' => [
        'driver' => 'daily',
        'path' => storage_path('logs/tenancy.log'),
        'level' => 'info',
        'days' => 30,
    ],
],
```

🆘 Support and Community
-----------------------

[](#-support-and-community)

### Getting Help

[](#getting-help)

- **📖 Documentation**:
- **🐛 Bug Reports**: [GitHub Issues](https://github.com/litepie/tenancy/issues)
- **💬 Discussions**: [GitHub Discussions](https://github.com/litepie/tenancy/discussions)
- **📧 Email Support**:
- **💼 Enterprise Support**:

### Contributing

[](#contributing)

We welcome contributions! Here's how you can help:

1. **🐛 Report Bugs**: Use GitHub Issues with detailed reproduction steps
2. **✨ Feature Requests**: Propose new features in GitHub Discussions
3. **📖 Documentation**: Help improve our documentation
4. **🧪 Testing**: Add test cases for edge cases
5. **💻 Code Contributions**: Submit pull requests with new features or fixes

### Development Setup

[](#development-setup)

```
# Clone the repository
git clone https://github.com/litepie/tenancy.git
cd tenancy

# Install dependencies
composer install

# Set up testing environment
cp .env.testing.example .env.testing
php artisan key:generate --env=testing

# Run tests
./vendor/bin/phpunit

# Run code style checks
./vendor/bin/pint

# Run static analysis
./vendor/bin/phpstan analyse
```

### Code Standards

[](#code-standards)

- **PSR-12** coding standard
- **PHPStan Level 8** static analysis
- **95%+ test coverage** requirement
- **Semantic versioning** for releases

📄 License
---------

[](#-license)

Litepie Tenancy is open-sourced software licensed under the **MIT License**. See the [LICENSE](LICENSE.md) file for details.

🙏 Credits and Acknowledgments
-----------------------------

[](#-credits-and-acknowledgments)

### Core Team

[](#core-team)

- **Litepie Development Team** - Core development and maintenance
- **Community Contributors** - Features, bug fixes, and documentation improvements

### Special Thanks

[](#special-thanks)

- **Laravel Team** - For the amazing framework that makes this possible
- **Spatie Team** - For inspiration from their multitenancy solutions
- **Contributors** - All the developers who have contributed code, tests, and documentation

### Sponsors

[](#sponsors)

We thank our sponsors who make this project possible:

- **Enterprise Sponsors** - Companies using Litepie Tenancy in production
- **Individual Sponsors** - Developers supporting open source

---

**Made with ❤️ by the Lavalite Team**

[⭐ Star us on GitHub](https://github.com/litepie/tenancy) | [🐛 Report Issues](https://github.com/litepie/tenancy/issues) | [💬 Join Discussions](https://github.com/litepie/tenancy/discussions)

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance39

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

2

Last Release

265d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1788735?v=4)[Renfos Technologies](/maintainers/Renfos)[@Renfos](https://github.com/Renfos)

---

Top Contributors

[![georgemjohn](https://avatars.githubusercontent.com/u/7950080?v=4)](https://github.com/georgemjohn "georgemjohn (2 commits)")

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/litepie-tenancy/health.svg)

```
[![Health](https://phpackages.com/badges/litepie-tenancy/health.svg)](https://phpackages.com/packages/litepie-tenancy)
```

###  Alternatives

[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k12.1M99](/packages/laravel-pulse)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[laravel/passport

Laravel Passport provides OAuth2 server support to Laravel.

3.4k85.0M532](/packages/laravel-passport)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k25.9M107](/packages/laravel-cashier)[laravel/scout

Laravel Scout provides a driver based solution to searching your Eloquent models.

1.7k49.4M479](/packages/laravel-scout)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
