Let’s get started with multi-tenancy in Laravel application with multiple database systems.
Multi-tenancy is the ability to provide your service to multiple users (tenants) from a single hosted instance of the application. This is contrasted with deploying the application separately for each user.
The multi-tenancy design pattern allows a single application to serve multiple tenants, each with their own set of needs and data. Applications that support several clients or organizations may find this to be quite helpful.
Making an Application for Multiple Tenants
Securing any common data, like reference tables, is crucial to data isolation since it keeps tenants from being able to view or alter each other’s data. If your application has a common list of product categories, for instance, you might wish to restrict access to administrators only or add other restrictions to make sure tenants are unable to edit the data.
Types of multi-tenancy
There are two types of multi-tenancy:
- single-database tenancy — tenants share one database and their data is separated using e.g.
where tenant_id = 1
clauses. - multi-database tenancy — each tenant has his own database
This package lets you do both, though it focuses more on multi-database tenancy because that type requires more work on the side of the package and less work on your side. Whereas for single-database tenancy you’re provided with a class that keeps track of the current tenant and model traits — and the rest is up to you.
for this tutorial, we will be using tenancy for Laravel package to achieve tenancy in our application.
Installation
First, require the package using composer:
composer require stancl/tenancy
Then, run the tenancy:install
command:
php artisan tenancy:install
This will create a few files: migrations, config file, route file and a service provider.
Let’s run the migrations:
php artisan migrate
Register the service provider in config/app.php
. Make sure it’s on the same position as in the code snippet below:
/*
* 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, // <-- here
Creating a tenant model
Now you need to create a Tenant model. The package comes with a default Tenant model that has many features, but it attempts to be mostly unopinionated and as such, we need to create a custom model to use domains & databases. Create the file app/Models/Tenant.php
like this:
<?php
namespace App\Models;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; }
Please note: if you have the models anywhere else, you should adjust the code and commands of this tutorial accordingly.
Now we need to tell the package to use this custom model. Open the config/tenancy.php
file and modify the line below:
'tenant_model' => \App\Models\Tenant::class,
Now most important part as you want to use admin part you have to define some central routes to get access of some routes but they should be secured by accessing for tenants to do that so We’ll make a small change to the app/Providers/RouteServiceProvider.php
file. Specifically, we’ll make sure that central routes are registered on central domains only.
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'); }
Call these methods manually from your RouteServiceProvider
‘s boot()
method, instead of the $this->routes()
calls.
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
$this->mapApiRoutes();
$this->mapWebRoutes();
});
}
Central domains
Now we need to specify the central domains. A central domain is a domain that serves your “central app” content, e.g. the landing page where tenants sign up. Open the config/tenancy.php
file and add them in:
'central_domains' => [
'saas.test', // Add the ones that you use. I use this one with Laravel Valet.
],
If you’re using Laravel Sail, no changes are needed, default values are good to go:
'central_domains' => [
'127.0.0.1',
'localhost',
],
Tenant routes
Your tenant routes will look like this by default:
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');
});
});
Let’s make a small change to dump all the users in the database, so that we can actually see multi-tenancy working. Open the file routes/tenant.php
and apply the modification below:
Route::get('/', function () {
dd(\App\Models\User::all());
return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id');
});
Migrations
To have users in tenant databases, let’s move the users
table migration (the file database/migrations/2014_10_12_000000_create_users_table.php
or similar) to database/migrations/tenant
. This will prevent the table from being created in the central database, and it will be instead created in the tenant database when a tenant is created — thanks to our event setup. If you have any other migrations that are necessary for your application, move those migrations as well.
Creating tenants
If you’re using Laravel Sail, please refer the Laravel Sail integration guide:
For testing purposes, we’ll create a tenant in tinker
— no need to waste time creating controllers and views for now.
$ php artisan tinker
>>> $tenant1 = App\Models\Tenant::create(['id' => 'foo']);
>>> $tenant1->domains()->create(['domain' => 'foo.localhost']);
>>>
>>> $tenant2 = App\Models\Tenant::create(['id' => 'bar']);
>>> $tenant2->domains()->create(['domain' => 'bar.localhost']);
Now we’ll create a user inside each tenant’s database:
App\Models\Tenant::all()->runForEach(function () {
App\Models\User::factory()->create();
});
In summary
We have looked at using Laravel to develop multi-tenant applications in this tutorial. and implement multi-tenancy with middleware. These methods let you design strong, adaptable applications that can easily service a variety of clients or businesses.