So, trying to scope what a user can see according to him belonging to a providers
model, I have this global scope defined :
class Provider extends Model
{
protected static function booted()
{
static::addGlobalScope(new ProviderScope);
}
...
The scope definition :
class ProviderScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
if(auth()->user()->providers->isNotEmpty()) {
$builder->whereIn('id', auth()->user()->providers->pluck('id'));
}
}
}
And so this gives me an error Undefined property: App\Models\User::$providers
which is obviously defined and works everywhere in the app.
My guess is that when trying to call auth()->user()->providers
in a function running on Provider's model booted
, it tries to initiate those providers, which triggers the booted function again, etc... I don't get why the error is an undefined property and not an infinite loop or something though. Am I right about the reason fir the error?
Also, given this need to scope the providers a user can see globally according to the providers he's already linked to, how would you solve it?
Thanks ahead!
Okay, so it was indeed an issue with trying to load a relationship from inside said relationship boot
events. It can't be loaded since it's not yet booted. Anyway, the solution I found is avoiding relationship usages altogether in global scopes.
class ProviderScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$providers = Provider::withoutGlobalScopes()
->join('provider_user', 'provider_user.provider_id', '=', 'providers.id')
->where('provider_user.user_id', auth()->user()->id)
->get();
if($providers->isNotEmpty()) {
$builder->whereIn('id', $providers->pluck('id'));
}
}
}
Keep in mind that whereHas
calls also rely on relationships, and will crash all the same. That's why we have to use a join
instead.