Ontwerp een Multi-Tenant Architectuur in Laravel
description
Met deze prompt kun je een schaalbare multi-tenant architectuur opzetten in Laravel, waardoor je efficiënter beheer van verschillende klanten mogelijk wordt. Het biedt een robuuste oplossing om data van verschillende tenants te scheiden, wat de veiligheid en performance ten goede komt.
prompt
Help mij bij het implementeren van een multi-tenant architectuur in mijn Laravel applicatie. Ik wil dat het systeem automatisch tenantgegevens identificeert en scheidt. Specificeer de tenant identificatie methode: <voer de methode voor tenant identificatie in (bijv. subdomei ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een overzichtelijke aanpak voor het implementeren van een multi-tenant architectuur in je Laravel applicatie, waarbij je subdomeinen gebruikt voor tenant identificatie en aparte databases voor elke tenant.
1. Routing en Tenant Detectie op Basis van Subdomein
- Zorg dat je DNS en je webserver (bijvoorbeeld Nginx) correct zijn ingesteld zodat subdomeinen naar je Laravel applicatie verwijzen.
- In Laravel kun je een routegroep maken die luistert op alle subdomeinen:
```php
// routes/web.php
Route::domain('{tenant}.example.com')->group(function () {
// Hier komen je routes die tenant-specifiek zijn
Route::get('/', [TenantController::class, 'index']);
// Voeg andere routes toe
});
```
2. Middleware voor Tenant Validatie en Database Switch
- Maak een middleware aan die de tenantnaam uit de URL haalt, controleert of de tenant bestaat, en vervolgens de database verbinding aanpast:
```bash
php artisan make:middleware IdentifyTenant
```
- In `app/Http/Middleware/IdentifyTenant.php`:
```php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Config;
use App\Models\Tenant; // Zorg dat je een Tenant model hebt
class IdentifyTenant
{
public function handle(Request $request, Closure $next)
{
$tenantSlug = $request->route('tenant');
// Controleer of de tenant bestaat
$tenant = Tenant::where('subdomain', $tenantSlug)->first();
if (!$tenant) {
abort(404, 'Tenant niet gevonden');
}
// Sla de tenant op in de sessie of in de container
app()->instance('tenant', $tenant);
// Stel de database verbinding in
Config::set('database.connections.tenant', [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'database' => $tenant->database_name,
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
]);
// Verander de default verbinding tijdelijk
\DB::setDefaultConnection('tenant');
return $next($request);
}
}
```
- Vergeet niet de middleware toe te voegen aan je kernel:
```php
// app/Http/Kernel.php
protected $routeMiddleware = [
// andere middleware
'tenant' => \App\Http\Middleware\IdentifyTenant::class,
];
```
3. Route Groepering met Middleware
- Pas je route aan om de middleware te gebruiken:
```php
Route::domain('{tenant}.example.com')->middleware('tenant')->group(function () {
Route::get('/', [TenantController::class, 'index']);
// andere routes
});
```
4. Tenant Model en Database Strategie
- Maak een Tenant model dat de gegevens van elke tenant bevat, inclusief de subdomeinnaam en database naam:
```php
// app/Models/Tenant.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tenant extends Model
{
protected $fillable = ['name', 'subdomain', 'database_name'];
}
```
- Elke tenant krijgt een aparte database. Zorg dat je voor elke tenant een database aanmaakt en dat de naam in je `tenants` tabel correct is.
5. Extra Overwegingen
- Initialiseer de databases en zet de juiste migraties klaar voor elke tenant.
- Overweeg een migratie script dat automatisch databases aanmaakt en schema’s opzet voor nieuwe tenants.
- Cache de tenantgegevens indien nodig voor betere prestaties.
Samenvatting:
- Gebruik subdomeinen voor tenant identificatie.
- Creëer een middleware die de subdomeinnaam leest, tenant valideert, en database verbinding aanpast.
- Gebruik aparte databases per tenant voor isolatie.
- Zorg voor een goede beheerinterface voor het toevoegen/verwijderen van tenants en databases.
Wil je dat ik je help met voorbeeld migraties, het maken van de Tenant tabel, of de exacte implementatie van de database setup?
provider: deepseek
model: deepseek-chat
Hier is een stapsgewijze implementatie voor een multi-tenant architectuur in Laravel met subdomein-identificatie en aparte databases:
## 1. Tenant Identificatie Middleware
Maak eerst een middleware voor tenantdetectie:
```bash
php artisan make:middleware IdentifyTenant
```
```php
<?php
// app/Http/Middleware/IdentifyTenant.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Models\Tenant;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
class IdentifyTenant
{
public function handle(Request $request, Closure $next)
{
$host = $request->getHost();
$subdomain = explode('.', $host)[0];
// Skip voor hoofddomain
if ($subdomain === 'www' || $subdomain === 'example') {
return $next($request);
}
// Zoek tenant op basis van subdomein
$tenant = Tenant::where('subdomain', $subdomain)->first();
if (!$tenant) {
abort(404, 'Tenant niet gevonden');
}
if (!$tenant->is_active) {
abort(403, 'Tenant is niet actief');
}
// Sla tenant op in request
$request->merge(['tenant' => $tenant]);
// Configureer database connection
$this->configureTenantDatabase($tenant);
return $next($request);
}
protected function configureTenantDatabase(Tenant $tenant)
{
Config::set('database.connections.tenant', [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => $tenant->database_name,
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
]);
DB::setDefaultConnection('tenant');
}
}
```
## 2. Tenant Model
```bash
php artisan make:model Tenant -m
```
```php
<?php
// app/Models/Tenant.php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Tenant extends Model
{
use HasFactory;
protected $fillable = [
'name',
'subdomain',
'database_name',
'is_active'
];
protected $casts = [
'is_active' => 'boolean'
];
}
```
## 3. Migratie voor Tenants
```php
<?php
// database/migrations/xxxx_xx_xx_create_tenants_table.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('subdomain')->unique();
$table->string('database_name')->unique();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('tenants');
}
}
```
## 4. Database Configuratie
Voeg toe aan je `.env`:
```
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=your_username
DB_PASSWORD=your_password
```
## 5. Middleware Registratie
Registreer de middleware in `app/Http/Kernel.php`:
```php
protected $middlewareGroups = [
'web' => [
// ... bestaande middleware
\App\Http\Middleware\IdentifyTenant::class,
],
'api' => [
// ... bestaande middleware
\App\Http\Middleware\IdentifyTenant::class,
],
];
```
## 6. Service Provider voor Database Management
```bash
php artisan make:provider TenantServiceProvider
```
```php
<?php
// app/Providers/TenantServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Models\Tenant;
use Illuminate\Support\Facades\Event;
use Illuminate\Database\Eloquent\Model;
class TenantServiceProvider extends ServiceProvider
{
public function boot()
{
// Event listener voor tenant creatie
Event::listen('tenant.created', function (Tenant $tenant) {
$this->createTenantDatabase($tenant);
});
}
protected function createTenantDatabase(Tenant $tenant)
{
$databaseName = $tenant->database_name;
// Creëer database
DB::statement("CREATE DATABASE IF NOT EXISTS {$databaseName}");
// Voer migraties uit voor nieuwe tenant
Artisan::call('migrate', [
'--database' => 'tenant',
'--path' => 'database/migrations/tenant',
'--force' => true
]);
}
}
```
## 7. Route Configuratie
Zorg ervoor dat je routes werken met subdomeinen:
```php
// routes/web.php
Route::domain('{tenant}.example.com')->group(function () {
Route::get('/', function ($tenant) {
return view('welcome');
});
// Je andere routes hier
});
```
## 8. Gebruik in Controllers
```php
public function index(Request $request)
{
$tenant = $request->tenant;
// Gebruik $tenant om tenant-specifieke data op te halen
}
```
## 9. Artisan Commando voor Tenant Creatie
```bash
php artisan make:command CreateTenant
```
```php
<?php
// app/Console/Commands/CreateTenant.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Tenant;
class CreateTenant extends Command
{
protected $signature = 'tenant:create {name} {subdomain}';
protected $description = 'Creëer een nieuwe tenant';
public function handle()
{
$tenant = Tenant::create([
'name' => $this->argument('name'),
'subdomain' => $this->argument('subdomain'),
'database_name' => 'tenant_' . $this->argument('subdomain')
]);
event('tenant.created', $tenant);
$this->info("Tenant {$tenant->name} succesvol aangemaakt!");
}
}
```
## 10. Gebruik
Maak een tenant aan:
```bash
php artisan tenant:create "Bedrijf A" "bedrijfa"
```
Deze implementatie zorgt voor:
- Automatische tenant detectie via subdomein
- Aparte database voor elke tenant
- Middleware validatie voor actieve tenants
- Dynamische database configuratie
- Eenvoudige tenant creatie via Artisan commands
Zorg ervoor dat je DNS correct is geconfigureerd voor subdomeinen en dat je database gebruiker rechten heeft om databases aan te maken.