Skip to main content

Multi-Tenancy in Filament 3

Premium
7:15

Text Version of the Lesson

Filament 3 comes with multi-tenancy support out of the box: the screenshot below shows how you can switch between teams/companies, see on the top-left:

This lets you quickly set up a multi-tenant application within a single database, just by configuring the panel. It even takes care of the switching between tenants for you.


Disclaimer: Tenancy is Hard.

It is important to note here that the meaning of multi-tenancy is different for everyone and that the demo in this lesson is just one of the possible implementations. It is not a one-size-fits-all solution. And the tenancy implementation also depends on your custom code more than on Filament core functionality.

Filament documentation says this:

Filament does not provide any guarantees about the security of your application. It is your responsibility to ensure that your application is secure. Please see the security section for more information.

With that said, here's our approach:

  • Configure your tenant Model. For example, Company, Organization, Team, etc.
  • Each User needs to have a belongsToMany relationship with that Tenant model. In other words, user may belong to many teams.
  • Apply the tenant to your PanelProvider to indicate that this panel is multi-tenant.
  • All of your Models must contain a tenant_id column, or an alternative, like company_id, organization_id, team_id, etc.
  • You must apply global scopes to all your Models to filter the data by the current tenant. Filament handles some of this, but that makes you prone to errors. It is better to do it yourself.
  • There is no multi-database support. All of your tenants will have to be in the same database. Switching between them is done by filtering the data by the current tenant, and each tenant is identified by its ID in the URL segment.

Telling Filament that Your Panel is Multi-Tenant

First, we have to create a multi-tenancy model, which in our case will be Company:

Migration

Schema::create('companies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
 
Schema::create('company_user', function (Blueprint $table) {
$table->foreignId('company_id')->constrained();
$table->foreignId('user_id')->constrained();
});

app/Models/Company.php

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
 
class Company extends Model
{
protected $fillable = [
'name'
];
 
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
}

Once we have our Company model, we can tell Filament that our panel is...

The Full Lesson is Only for Premium Members

Want to access all of our courses? (30 h 09 min)

You also get:

55 courses
Premium tutorials
Access to repositories
Private Discord
Get Premium for $129/year or $29/month

Already a member? Login here

Comments & Discussion

CP
Claudio Pereira ✓ Link copied!

Povilas, I'm creating a document manager SAAS for accountants. In it I will have some Global Templates that will serve to classify the records, which must be accessed by all Tenants (Document Types, Categories) but which cannot be edited by Tenants. These are tables that can only be edited by an Admin and must not be filtered by a global scope. Can you point me to documentation that helps me achieve this?

PK
Povilas Korop ✓ Link copied!

Maybe the section about Disabling global scopes?

CP
Claudio Pereira ✓ Link copied!

Thanks! I'll check this.

S
Sjoerd24 ✓ Link copied!

I have the same problem, I try to setup multi tenancy but I have some tables that are for general usage and shouldnt be scoped. Or i want (same as with claudio) that admins can view everything.

I tried adding this to the ModelResource.php:

public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()->withoutGlobalScopes([Company::class]);
}

or this:

protected static ?string $tenantOwnershipRelationshipName = null;

I tried using a ApplyTenantScopes and then using something like this:

Model::withoutGlobalScope(Company::class);

But nothing seems to work. I saw in your older documentation about Filament 2.0 a possible solution (https://laraveldaily.com/post/multi-tenancy-laravel-filament-simple) but I don't know what a good soluton for 3.0 would be.

PK
Povilas Korop ✓ Link copied!

To be perfectly honest, the way how Filament v3 Multi-Tenancy is done, personally to me, seems not perfect at the moment, so I don't have a quick answer. But will try to debug and comment with possible solutions a bit later.

S
Sjoerd24 ✓ Link copied!

Ok so with the help of Dan Harrin and the filament github codebase I found a solution, copy paste the following code in the Resource that you want to exclude from the tenant scope:

public static function scopeEloquentQueryToTenant(Builder $query, ?Model $tenant): Builder
{
return $query;
}

And include in the top of the same file:

use Illuminate\Database\Eloquent\Model;

You basicly override the tenantscope function and return it cleanly. I haven't found problems yet, so I think (hope) this works as intended. :)

M
Modestas ✓ Link copied!

To remove the scopes from your Resource, you can do this:

CategoryResource -> Create new method:

public static function getEloquentQuery(): Builder
{
return static::getModel()::query()->withoutGlobalScopes();
}

This will override the Query creation from Filament and return you a new instance without Tenant information. Will that work everywhere? Not sure... Filament multi-tenancy does not really support:

  • Global resources for everyone
  • Admin roles

It is more or less designed for independant tenants to live within your application and never interact.

ps. You can technically create an admin by creatin a new panel that doesn't have tenancy applied to it. It duplicates the resources and everything else but... It should work :)

J
johnogle ✓ Link copied!

Thanks, Povilas. Gang, I agree that Tenancy is hard! I had a minor problem figuring out which of the many "Builder" classes to import into ApplyTenantScopes and :

use Illuminate\Contracts\Database\Eloquent\Builder;

seemed to work best for me after trial and error. Maybe this will help some other newbie!

PK
Povilas Korop ✓ Link copied!

Thanks, sorry for the confusion! Now added three missing import classes to the lesson, it wasn't only Builder, the correct import from the docs:

use Closure;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
J
jcastellvi ✓ Link copied!

To be able to share a record between several teams, it would be possible to use a Pivot Table?

PK
Povilas Korop ✓ Link copied!

I don't think that currently it's possible in Filament, at least I haven't seen it in the documentation.

PB
Pedro Barradas ✓ Link copied!

Hi Povilas,

I tried to replicate tenancy at this project, and so far so good....but when I need to add a product, I get the error that General error: 1364 Field 'company_id' doesn't have a default value, what is missing to me?

Thank You!

PB
Pedro Barradas ✓ Link copied!

I've discover the problem and now its working :) I found a video of you at youtube :) "Simple Multi-Tenancy in Filament: TWO Ways" Thank You!

K
kaleemullah ✓ Link copied!

Great article .

JC
Jon Cody ✓ Link copied!

Alright, so with multi tenacy, there is also an option where users or other models would only be able to see their own data. Thus the "swtiching" would be unnecessary. Is the process the same for this requirement?

For example if i make a user panel, which allows users to update and manage content and settings related to them? Or a group panel. Think social media usage primarily. But may be good for courses, stores or other use cases.

This is a big topic that I hope you might have or be willing to make a tutorial for. Also considering larger tables that you may want to split into several pages instead of tabs. Can you have multiple resource pages all using the same table but handling different fields? Example if my user table has a group of fields for login data, then another group for demographic data, or special interests. or whatever that may fit within a single table.

M
mpetersen ✓ Link copied!

Hi there,

I'm implementing a solution where we need 1 user per tenant, and not a user can belong to many tenants. Is there an example I can look at?

M
Modestas ✓ Link copied!

Some of our other articles might help you:

https://laraveldaily.com/tag/multi-tenancy?source=search

In general, it's as simple as adding the tenant_id and it's scopes :)

HH
Hasan Hafiz ✓ Link copied!

Can you please share full code repository? I was unable to implement tenatcy. So, it would be helpful to me as well as others.

Thanks

M
Modestas ✓ Link copied!

In the last lesson you can see the repository: https://github.com/LaravelDaily/Filament3-Course-Main

HH
Hasan Hafiz ✓ Link copied!

I look at your repo but could not find any code related to Tenant. For example, I did not find Company resource, Updated User Model that are responsible for Tenant.

Can you please check again and give me updated link!

Or can you please give me a screenshot?

Thanks.

M
Modestas ✓ Link copied!

Have you switched the branch? :)

There's quite a few of them there, but this one should have the tenancy implementation:

https://github.com/LaravelDaily/Filament3-Course-Main/tree/feature/multi-tenancy

AR
Aditya Rao ✓ Link copied!

Hi there.

Can I have panels that support multiple levels of tenancy. for example: Primary Tenant: Companies Sub Tenants: Projects

Any suggestion would be helpful.

Thanks

M
Modestas ✓ Link copied!

I'm not exactly sure if that is possible. As far as I know Filament works with one level of tenancy, so not sure how would 2 or more levels work.

You could of course go in and code all of that logic manually, but you might have some headaches.

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.