Multi-tenancy is the cornerstone of SaaS applications. Laravel 5's architecture makes implementing tenant isolation elegant and maintainable. At ZIRA Software, we've built numerous multi-tenant platforms using these patterns.
Multi-Tenancy Approaches
Multi-Tenancy Strategies
├── Database per Tenant
│ ├── Complete isolation
│ ├── Easy backup/restore
│ └── Higher infrastructure cost
├── Shared Database, Separate Schemas
│ ├── Good isolation
│ ├── PostgreSQL native support
│ └── Medium complexity
└── Shared Database, Shared Schema
├── Tenant ID column
├── Lowest cost
└── Requires careful scoping
Shared Schema Implementation
// app/Models/Tenant.php
class Tenant extends Model
{
protected $fillable = ['name', 'domain', 'database'];
public function users()
{
return $this->hasMany(User::class);
}
}
// app/Traits/BelongsToTenant.php
trait BelongsToTenant
{
protected static function bootBelongsToTenant()
{
static::creating(function ($model) {
if (session()->has('tenant_id')) {
$model->tenant_id = session('tenant_id');
}
});
static::addGlobalScope('tenant', function ($query) {
if (session()->has('tenant_id')) {
$query->where('tenant_id', session('tenant_id'));
}
});
}
}
// Usage in models
class Project extends Model
{
use BelongsToTenant;
protected $fillable = ['name', 'description', 'tenant_id'];
}
Tenant Resolution Middleware
// app/Http/Middleware/ResolveTenant.php
class ResolveTenant
{
public function handle($request, Closure $next)
{
$host = $request->getHost();
// Subdomain-based tenant resolution
$subdomain = explode('.', $host)[0];
$tenant = Tenant::where('subdomain', $subdomain)->first();
if (!$tenant) {
abort(404, 'Tenant not found');
}
session(['tenant_id' => $tenant->id]);
config(['app.tenant' => $tenant]);
return $next($request);
}
}
// app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// ... other middleware
\App\Http\Middleware\ResolveTenant::class,
],
];
Database per Tenant
// config/database.php
'connections' => [
'tenant' => [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => '', // Set dynamically
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
],
],
// app/Services/TenantManager.php
class TenantManager
{
public function setConnection(Tenant $tenant)
{
config([
'database.connections.tenant.database' => $tenant->database
]);
DB::purge('tenant');
DB::reconnect('tenant');
}
public function createTenantDatabase(Tenant $tenant)
{
$database = 'tenant_' . $tenant->id;
DB::statement("CREATE DATABASE {$database}");
// Run migrations
Artisan::call('migrate', [
'--database' => 'tenant',
'--path' => 'database/migrations/tenant'
]);
$tenant->update(['database' => $database]);
}
}
Tenant-Aware Queues
// app/Jobs/TenantAwareJob.php
abstract class TenantAwareJob implements ShouldQueue
{
protected $tenantId;
public function __construct()
{
$this->tenantId = session('tenant_id');
}
public function handle()
{
session(['tenant_id' => $this->tenantId]);
$this->process();
}
abstract protected function process();
}
// Usage
class ProcessTenantReport extends TenantAwareJob
{
protected function process()
{
// Tenant scope automatically applied
$projects = Project::all();
// Generate report...
}
}
Conclusion
Multi-tenancy in Laravel requires careful planning around data isolation, performance, and scalability. Choose the approach that matches your security requirements and budget constraints.
Building a SaaS platform? Contact ZIRA Software for multi-tenancy architecture consulting.