Back to Course |
Laravel 11 Multi-Tenancy: All You Need To Know

spatie / multi-tenancy: Bare Essentials for Multi-Tenancy

The last package we will cover in the multi-tenancy single database comes from the well-known company Spatie. It has a name of spatie/laravel-multitenancy and has a slightly different philosophy than others. The key philosophy of this package is that it should provide only the bare essentials to enable multi-tenancy and to make your application tenant aware.

What does that mean? The package will provide the minimum functionality for identifying the tenant and switching between them. Still, it will leave your custom application logic to your custom application and will provide only what it should.

In other words, this package has fewer features than we covered previously, especially if we're talking about a single database application. It has quite a lot to offer for multi-database, and we will cover that in the following lessons.

Let's install it in our single database and use roughly the same logic for creating tenants and protecting our data for projects and tasks.


So, as usual, we install the package via composer and then publish the configuration file.

composer require spatie/laravel-multitenancy
php artisan vendor:publish --provider="Spatie\Multitenancy\MultitenancyServiceProvider" --tag="multitenancy-config"

The next step is to choose which database type we will use, single or multi-database. This lesson will use the single database. First, we must publish the migrations.

php artisan vendor:publish --provider="Spatie\Multitenancy\MultitenancyServiceProvider" --tag="multitenancy-migrations"
php artisan migrate --path=database/migrations/landlord

The package offers a system of classes of tenant finders. It may be a domain tenant finder or your custom tenant finder.

How do we determine the current tenant? In the config/multitenancy.php, the tenant_finder is set to null. Let's set finder to a DomainTenantFinder class from the package.

config/tenancy.php:

return [
'tenant_finder' => \Spatie\Multitenancy\TenantFinder\DomainTenantFinder::class,
 
// ...

The DomainTenantFinder determines the tenant by a host, which is a domain.

Let's create the tenant when registering. To create a tenant, I first made a Tenant Model, which extends the Tenant Model from the package.

app/Models/Tenant.php:

class Tenant extends \Spatie\Multitenancy\Models\Tenant
{
protected $fillable = [
'name',
'domain',
'database',
];
}

database/migrations/xxx_create_tenant_user_table.php:

Schema::create('tenant_user', function (Blueprint $table) {
$table->foreignId('tenant_id')->constrained();
$table->foreignId('user_id')->constrained();
});

app/Models/User.php:

class User extends Authenticatable
{
// ...
 
public function tenants(): BelongsToMany
{
return $this->belongsToMany(Tenant::class);
}
}

app/Http/Controllers/Auth/RegisteredUserController.php:

class RegisteredUserController extends Controller
{
// ...
 
public function store(Request $request): RedirectResponse
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
 
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
 
$tenant = Tenant::create([
'name' => $request->name . ' Team',
'domain' => $request->subdomain . '.' . config('session.domain'),
'database' => $request->subdomain
]);
$user->tenants()->attach($tenant->id);
 
event(new Registered($user));
 
Auth::login($user);
 
return redirect(route('dashboard', absolute: false));
return redirect('http://' . $request->subdomain . '.' . config('session.domain') . route('dashboard', absolute: false));
}
}

resources/views/auth/register.blade.php:

// ...
 
<!-- Subdomain -->
<div class="mt-4">
<x-input-label for="subdomain" :value="__('Subdomain')" />
 
<x-text-input id="subdomain" class="block mt-1 w-full" type="text" name="subdomain" :value="old('subdomain')" required />
</div>
 
// ...

And we must define the session domain in the .env.

.env:

// ...
 
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=tenancy.test ##
 
// ...

Until now, the code is very similar to what we have already done in the previous lessons. After registering, we are redirected to the correct subdomain.

But from here, all the tasks related to our application and making models multi-tenantable are on us. The package will only go up to this line.


This is the spatie/laravel-multitenancy package for a single database. It will help you determine the tenant by domain or other logic you provide. But from there, you're on your own, creating your application.

You can find the source code for the single database example using the spatie/laravel-multitenancy package on GitHub.