Installation
composer require hyn/multi-tenant
Publish the package config and migrations:
php artisan vendor:publish --provider="Hyn\MultiTenant\MultiTenantServiceProvider"
php artisan migrate
Basic Tenant Setup
tenant() middleware in app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
// ...
\Hyn\MultiTenant\Middleware\InitializeTenancyByDomain::class,
],
];
.env:
TENANCY_DOMAINS=tenant1.example.com,tenant2.example.com
First Use Case: Isolating Tenant Data
tenant() helper to switch contexts:
$tenant = tenant('tenant1');
$tenant->activate(); // Automatically scoped queries to tenant1's database
tenant1.example.com) and verify data isolation.Tenant Activation
InitializeTenancyByDomain to auto-activate tenants via subdomains.tenant()->activate() in API routes or CLI scripts:
$tenant = tenant()->findOrFail($request->tenant_id);
$tenant->activate();
Database Scoping
$users = User::all(); // Only returns users for the active tenant
User::withoutGlobalScopes()->get(); // Unscoped query
Middleware Integration
InitializeTenancyByDomain for non-domain logic (e.g., API keys):
public function resolve()
{
$tenant = Tenant::where('api_key', $request->header('X-Tenant-Key'))->first();
return $tenant ?: null;
}
CLI & Testing
php artisan tinker
>>> tenant('tenant1')->activate();
Tenancy::actingAs() to simulate tenants:
Tenancy::actingAs(Tenant::find(1), function () {
// Test as tenant1
});
Database Connection Confusion
activate() before queries.tenant()->activate() in middleware or services.DB::connection()->getDatabaseName() to verify the active tenant’s DB.Migration Pitfalls
php artisan tenant:migrate tenant1 or wrap in Tenancy::actingAs().Caching Quirks
Tenancy::actingAs() in cached views.Queue Jobs
Tenancy::actingAs($tenant, function () {
YourJob::dispatch();
});
Dynamic Tenant Creation
Tenant::create() with a database name (e.g., tenant_db_1):
$tenant = Tenant::create([
'identifier' => 'tenant1',
'database' => 'tenant_db_1',
]);
Shared vs. Isolated Data
shared() to mark models for cross-tenant access:
class Setting extends Model
{
use \Hyn\MultiTenant\Traits\UsesTenancy;
protected $shared = true;
}
Performance
tenant_id to frequently queried tables for faster joins.pgsql or mysql with connection pooling to avoid overhead.Debugging
public function handle($request, Closure $next)
{
\Log::info('Active Tenant:', ['tenant' => tenant()]);
return $next($request);
}
tenancy.activated/tenancy.deactivated:
event(new TenancyActivated($tenant));
Extending Tenancy
Hyn\MultiTenant\Models\Tenant for custom fields.resolve() in middleware for custom resolution logic.How can I help you explore Laravel packages today?