Multi-tenant: search across all products
I have a multitenant application. My tenant is called 'organization'. I want a user to be able to ask information about a product by sending a message. I have therefore a MessageResource with below snippet. The idea is that a user can search across all products in the database, not only the products that he's owning (=assigned to his organization).
Below code works but only shows the products linked to the organization.
Select::make('product_id')
->label('Product')
->columnSpan(1)
->searchable()
->getSearchResultsUsing(function ($search) {
if (strlen($search) < 3) {
return [];
}
return Product::where('name', 'like', "%{$search}%")
->limit(50)
->get()
->pluck('name', 'id');
})
->getOptionLabelUsing(fn ($value): ?string => Product::find($value)?->name)
->disabled(false),
I also have the following:
protected static function booted(): void
{
static::addGlobalScope('organization', function (Builder $query) {
if (auth()->hasUser()) {
$query->where('organization_id', auth()->user()->organization_id);
}
});
}
This explains of course why I only see the products for the organization.
I read that Filament does scoping to tenant automatically, so the above global scope seems redundant. Still I wanted to have it to have a secure feeling.
Two quesions:
1) can I safely remove the global scope?
2) Is there a way I could still enforce a scope, except for this one resource (MessageResource)?
Any suggestions?Solution:Jump to solution
You would combine those. Sorry I was focusing on the resource itself but you actually need it on the select π
8 Replies
You can leave it in place just set the resource scope to be false
can I safely remove the global scope?Not sure whether the constraint is applied everywhere. It is applied for resources, but can't find anything for Select relationships.
2) Is there a way I could still enforce a scope, except for this one resource (MessageResource)?If you have a global scope: Overwrite
getEloquentQuery()
and add ->withoutGlobalScope('organization')
.
To disable scoping of Filament overwrite public static function isScopedToTenant()
and return false.Confused a bit:
1) Not having a
static::addGlobalScope
on the Product Model works
2) Having a static::addGlobalScope
on the Product Model does not give all products (which is understandable). Overwriting public static function isScopedToTenant()
to false, it not giving all the products.
3) Having a static::addGlobalScope
on the Product Model and overwrite the getEloquentQuery()
in the MessageResource is sth I cannot do as it is already used for the MessageResource (the retrieval of the products is done in the MessageResource and there is already a getEloquentQuery()
in place)
4) When I nevertheless add the getEloquentQuery()
with a return Animal::query()->withoutGlobalScope('organization');
I get only the Products belonging to the organization again.Overwriting public static function isScopedToTenant() to false, it not giving all the products.That only overwrites Filaments scoping. Not your global scope
I cannot do as it is already used for the MessageResource (the retrieval of the products is done in the MessageResource and there is already a getEloquentQuery() in place)I don't understand. When you already have a
getEloquentQuery()
then just add the code there?
When I nevertheless add the getEloquentQuery() with a return Animal::query()->withoutGlobalScope('organization'); I get only the Products belonging to the organization again.You still need
isScopedByTenant()
do undo Filament's scoping
Summary: If you only use Filament's scoping, just change isScopedByTenant
. If you have an additional global scope, you need to additionally remove that via getEloquentQuery()
It might be because it's Friday eve here, but how would I be able to combine the below. The first one is what is existing already and the second one I need to add.
public static function getEloquentQuery(): Builder
{
return Topic::query();
}
public static function getEloquentQuery(): Builder
{
return Product::query()->withoutGlobalScope('organization');
}
Solution
You would combine those. Sorry I was focusing on the resource itself but you actually need it on the select π
For the select I guess it might be enough to set
getSearchResultsUsing()
and return a query without the scopeHmmm, stupid I did not think about it, but it works
->getSearchResultsUsing(function ($search) {
if (strlen($search) < 3) {
return [];
}
return Product::where('name', 'like', "%{$search}%")->withoutGlobalScope('organization')
->limit(50)
->get()
->pluck('name', 'id');
})