Skip to content

Getting started

This page will go over installing the package, as well as building a simple multi-domain multi-database demo, where each tenant has different users.

It’s recommended to follow this page in a fresh Laravel 11 application first, so that it’s easier for you to follow and make sense of the steps described here. After that, you should be able to confidently implement the package into an existing application.

Installing the package

To install the package, require it using composer:

Terminal window
composer require stancl/tenancy

Then, run the tenancy:install command:

Terminal window
php artisan tenancy:install

That will create the following files:

  • Directoryapp
    • DirectoryProviders
      • TenancyServiceProvider.php event listeners and route registration
  • Directoryconfig
    • tenancy.php where the package behavior is configured
  • Directorydatabase
    • Directorymigrations
      • 2019_09_15_000010_create_tenants_table.php
      • 2019_09_15_000020_create_domains_table.php
  • Directoryroutes
    • tenant.php

The three highlighted files (of the five total) are required. The others can sometimes be removed. We’ll cover that later.

After these files are created, add TenancyServiceProvider to your bootstrap/providers.php array, directly below AppServiceProvider:

bootstrap/providers.php
return [
App\Providers\AppServiceProvider::class,
App\Providers\TenancyServiceProvider::class,
];

With this, the package is installed. However, to demonstrate usage, we should do a few more things.

You can feel free to revert or change these, but I recommend following this guide in full to get a working multi-tenant app before you start customizing things.

The setup we’ll be using here is multi-domain multi-tenant. It’s what works best for the vast majority of applications, and is also the simplest, so it works great for our purposes here.

Setting up routing

Without any changes, Laravel is registering your web.php routes in bootstrap/app.php, and your tenant.php routes are being registered in your TenancyServiceProvider. However, there’s an edge case: which route should be used on a path defined in both web.php and tenant.php — such as /?

routes/web.php
Route::get('/', function () {
return view('welcome');
});
routes/tenant.php
Route::get('/', function () {
return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id');
});

To solve this, we need to scope the routes to their domains. tenant.php is already doing this using the PreventAccessFromUnwantedDomains middleware which prevents access from any domains defined in the tenancy.identification.central_domains config. But we also need to scope the routes in web.php.

There are two ways to do this. The first is to wrap your web.php routes in a loop like this:

web.php
foreach (config('tenancy.identification.central_domains') as $domain) {
Route::domain($domain)->group(function () {
// your actual routes
});
}

The other approach is a bit more complicated but will result in simpler route files. See Routing for more details.

Running migrations

The tenancy:install command we ran earlier created two migrations. Let’s run them:

Terminal window
php artisan migrate

Now that we have the tenants and domains tables created, let’s configure our Tenant model.

Creating a Tenant model

Create a new model:

app/Models/Tenant.php
<?php
namespace App\Models;
use Stancl\Tenancy\Database\Models\Tenant as BaseTenant;
use Stancl\Tenancy\Database\Concerns\HasDatabase;
use Stancl\Tenancy\Database\Concerns\HasDomains;
use Stancl\Tenancy\Database\Contracts\TenantWithDatabase;
class Tenant extends BaseTenant implements TenantWithDatabase
{
use HasDatabase, HasDomains;
}

And configure it as the tenant model the package should use:

config/tenancy.php
return [
/**
* Configuration for the models used by Tenancy.
*/
'models' => [
'tenant' => App\Models\Tenant::class,

Adding a tenant migration

Let’s copy the create_users_table migration to the database/migrations/tenant folder, which got created automatically when you ran php artisan tenancy:install:

Terminal window
cp database/migrations/0001_01_01_000000_create_users_table.php \
database/migrations/tenant/0001_01_01_000000_create_users_table.php

In a fresh Laravel 11 application, your database/migrations should now look like this:

  • Directorydatabase/migrations
    • 0001_01_01_000000_create_users_table.php
    • 0001_01_01_000001_create_cache_table.php
    • 0001_01_01_000002_create_jobs_table.php
    • 2019_09_15_000010_create_tenants_table.php
    • 2019_09_15_000020_create_domains_table.php
    • Directorytenant
      • 0001_01_01_000000_create_users_table.php

Now that we’ve moved the migration to the database/migrations/tenant folder, the tables defined in that migration will get created whenever a tenant is created.

Creating a sample tenant

Now that our Tenant model is created and configured, let’s go ahead and use it to create an actual tenant:

php artisan tinker
$tenant = App\Models\Tenant::create();
$tenant->createDomain('tenant1.example.test');

Now that we’ve created our tenant along with its domain, we can open it in the browser:

  • example.test should serve your / route from web.php (the Laravel welcome page by default)
  • tenant1.example.test should show: This is your multi-tenant application. The id of the current tenant is ...

The output demonstrates that the tenant was identified using the domain, but let’s also verify that the tenant is using their own database:

routes/tenant.php
Route::get('/', function () {
dd(\DB::connection()->getDatabaseName());
return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id');
});

If you visit tenant1.example.test again, you should see tenant followed by a UUID.

Now let’s try to add some data to the tenant database:

php artisan tinker
$tenant = App\Models\Tenant::first();
tenancy()->initialize($tenant);
App\Models\User::create(['name' => 'foo', 'email' => 'foo@example.test', 'password' => bcrypt('password')]);
$tenant2 = App\Models\Tenant::create();
$tenant2->createDomain('tenant2.example.test'); // make sure the domain is correct
tenancy()->initialize($tenant2);
App\Models\User::create(['name' => 'bar', 'email' => 'bar@example.test', 'password' => bcrypt('password')]);
tenancy()->end();
App\Models\User::create(['name' => 'baz', 'email' => 'baz@example.test', 'password' => bcrypt('password')]);

Let’s change the route files:

routes/tenant.php
Route::get('/', function () {
dd(\DB::connection()->getDatabaseName());
dd(\App\Models\User::first()->email);
return 'This is your multi-tenant application. The id of the current tenant is ' . tenant('id');
});
routes/web.php
Route::get('/', function () {
return view('welcome');
dd(\App\Models\User::first()->email);
});

Now you should see:

  • foo@example.test on tenant1.example.test
  • bar@example.test on tenant2.example.test
  • baz@example.test on example.test (central domain)

And with that, you have a fully working multi-domain multi-database application!

To learn how to configure the package for your exact needs, make sure to read these pages next: