Implementing Multi‑Tenant Architecture in Laravel with the Stancl Tenancy Package
This guide walks through installing the Stancl Tenancy package, configuring Laravel for multi‑tenant support, defining a custom tenant model, setting central and tenant domains, adjusting route providers, running migrations, and creating tenants via Artisan, enabling fully isolated SaaS databases without altering existing application code.
The article introduces the Stancl Tenancy package, a plug‑and‑play solution that adds multi‑tenant capabilities to a Laravel application without requiring extensive code changes.
Installation is performed via Composer: composer require stancl/tenancy After installing, run the package’s installer: php artisan tenancy:install This command generates migration files, configuration files, route files, and a service provider.
Next, execute the migrations to create the necessary tables: php artisan migrate Register the newly created service provider in config/app.php (add the provider to the providers array):
/* Application Service Providers... */
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\TenancyServiceProvider::class,Define a custom tenant model ( app/Tenant.php) that extends the base tenant and uses the HasDatabase and HasDomains traits:
<?php
namespace App;
use Stancl\Tenancy\Database\Models\Tenant as BaseTenant;
use Stancl\Tenancy\Contracts\TenantWithDatabase;
use Stancl\Tenancy\Database\Concerns\HasDatabase;
use Stancl\Tenancy\Database\Concerns\HasDomains;
class Tenant extends BaseTenant implements TenantWithDatabase
{
use HasDatabase, HasDomains;
}Tell the package to use this model by editing config/tenancy.php: 'tenant_model' => \App\Tenant::class, The application is split into two logical parts: a central app (login page, admin dashboard) and a tenant app (business logic). Adjust the route service provider so that routes are only loaded for the appropriate domain:
protected function mapWebRoutes()
{
foreach ($this->centralDomains() as $domain) {
Route::middleware('web')
->domain($domain)
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
}
protected function mapApiRoutes()
{
foreach ($this->centralDomains() as $domain) {
Route::prefix('api')
->domain($domain)
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
}
protected function centralDomains(): array
{
return config('tenancy.central_domains');
}Define the central domains in config/tenancy.php (e.g., saas.test) and protect tenant routes with the PreventAccessFromCentralDomains middleware:
Route::middleware(['web', InitializeTenancyByDomain::class, PreventAccessFromCentralDomains::class])
->group(function () {
Route::get('/', function () {
return 'This is your multi-tenant application. The id of the current tenant is '.tenant('id');
});
});Optionally dump all users for debugging:
Route::get('/', function () {
dd(\App\User::all());
return 'This is your multi-tenant application. The id of the current tenant is '.tenant('id');
});Move tenant‑specific migrations to database/migrations/tenant so that tables are created per tenant database.
Finally, create tenants via Artisan Tinker:
$tenant1 = Tenant::create(['id' => 'foo']);
$tenant1->domains()->create(['domain' => 'foo.saas.test']);
$tenant2 = Tenant::create(['id' => 'bar']);
$tenant2->domains()->create(['domain' => 'bar.saas.test']);Populate each tenant’s database with users:
App\Tenant::all()->runForEach(function () {
factory(App\User::class)->create();
});Visiting foo.saas.test or bar.saas.test now shows isolated user data, demonstrating that the application code remains unchanged while the tenancy package handles database switching automatically.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Laravel Tech Community
Specializing in Laravel development, we continuously publish fresh content and grow alongside the elegant, stable Laravel framework.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
