PHP code example of litepie / tenancy

1. Go to this page and download the library: Download litepie/tenancy library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

litepie / tenancy example snippets


'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,
        ],
    ],
],

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

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 

// routes/web.php
Route::middleware(['tenant.oller::class, 'index']);
    Route::resource('orders', OrderController::class);
    Route::resource('customers', CustomerController::class);
});

// routes/api.php
Route::middleware(['api', 'tenant.nue']);
    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 

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'));
    }
}

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);

// 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' => [
    'strategy' => 'separate',
    'auto_create_database' => true,
    'auto_migrate' => true,
    'tenant_database_prefix' => 'client_',
    'connection_pooling' => true,
    'max_connections' => 100,
],

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

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);

// 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,
],

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', '<', now())
            ->with('customer')
            ->get();
        
        foreach ($overdueInvoices as $invoice) {
            // Send reminder email
            Mail::to($invoice->customer->email)
                ->send(new InvoiceReminder($invoice));
            
            // Log the reminder
            $invoice->reminders()->create([
                'sent_at' => now(),
                'type' => 'overdue',
            ]);
        }
        
        Log::info("Sent {$overdueInvoices->count()} invoice reminders", [
            'tenant_id' => $tenant->id,
            'tenant_name' => $tenant->name,
        ]);
    }
    
    public function failed(\Throwable $exception)
    {
        Log::error('Invoice reminder job failed', [
            'tenant_id' => tenancy()->current()?->id,
            'exception' => $exception->getMessage(),
        ]);
    }
}

// config/tenancy.php
'queue' => [
    'tenant_aware' => true,
    'per_tenant_workers' => true,
    'tenant_queue' => 'tenant-{tenant_id}',
    'serialize_tenant' => true,
    'max_retries' => 3,
    'retry_delay' => 60,
],

use Litepie\Tenancy\Events\TenantActivated;
use Litepie\Tenancy\Events\TenantDeactivated;
use Litepie\Tenancy\Events\TenantCreated;
use Litepie\Tenancy\Events\TenantDeleted;

// In your EventServiceProvider
protected $listen = [
    TenantActivated::class => [
        ConfigureTenantSettings::class,
        InitializeTenantServices::class,
        LogTenantAccess::class,
    ],
    TenantCreated::class => [
        SetupTenantDatabase::class,
        CreateTenantDirectories::class,
        SendWelcomeEmail::class,
    ],
    TenantDeactivated::class => [
        CleanupTenantCache::class,
        LogTenantExit::class,
    ],
];

class ConfigureTenantSettings
{
    public function handle(TenantActivated $event): void
    {
        $tenant = $event->tenant;
        
        // Configure tenant-specific application settings
        config([
            'app.name' => $tenant->getConfig('app_name', config('app.name')),
            'app.timezone' => $tenant->getConfig('timezone', 'UTC'),
            'mail.from.name' => $tenant->getConfig('mail_from_name'),
            'mail.from.address' => $tenant->getConfig('mail_from_address'),
        ]);
        
        // Set up tenant-specific services
        if ($tenant->hasConfig('stripe_key')) {
            app()->instance('stripe', new StripeService(
                $tenant->getConfig('stripe_key'),
                $tenant->getConfig('stripe_secret')
            ));
        }
        
        if ($tenant->hasConfig('analytics_key')) {
            app()->instance('analytics', new GoogleAnalytics(
                $tenant->getConfig('analytics_key')
            ));
        }
    }
}

// Automatic tenant scoping - prevents cross-tenant data leaks
$orders = Order::all(); // Only returns current tenant's orders

// Explicit tenant filtering when needed
$orders = Order::forTenant($specificTenant)->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();

// 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,
],

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);
});

// 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(),
]);

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);

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';
    }
}

// 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

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' => '[email protected]',
        ]);
        
        $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'));
    }
}

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);
    }
}

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 

// 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,
    ],
],

// 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',
    ],
],

// 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,
    ],
],

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

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

// 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,
],

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

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

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

// config/logging.php
'channels' => [
    'tenancy' => [
        'driver' => 'daily',
        'path' => storage_path('logs/tenancy.log'),
        'level' => 'info',
        'days' => 30,
    ],
],
bash
# Publish configuration file
php artisan vendor:publish --tag=tenancy-config

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

# Run migrations
php artisan migrate
env
# === 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
bash
# Complete system health check
php artisan tenant:diagnose

# Check specific components
php artisan tenant:diagnose --check-config
php artisan tenant:diagnose --check-time metrics
php artisan tenant:monitor --real-time
nginx
# Nginx configuration for tenant routing
server {
    listen 80;
    server_name ~^(?<tenant>.+)\.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;
    }
}
bash
# 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);
bash
# 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();
bash
# Check queue configuration
php artisan tenant:diagnose --check-config

# Monitor queue workers
php artisan queue:monitor