laravellaravel-9global-scope

Laravel - Global scopes and relationship check -undefined property


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!


Solution

  • 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.