I am trying to access the currently authenticated user within the query scope class. I am using 3 custom guards for 3 types of users. Each of the user models contains a region_id column, value of which I want to access to filter virtually all models in the app.
When I call the Auth::guard($guard)->user() within the query scope class the value is always null, even though the user is authenticated. Information about the authenticated user is accessible from e.g. controllers, so it is not the issue of a user not being authenticated.
It seems to me like the authentication check occurs _after_ the query scopes are executed.
My query scope class:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class RegionScope implements Scope
{
protected $region_id;
public function __construct() {
Log::debug('Region query scope constructor just started');
$guards = array_keys(config('auth.guards'));
foreach ($guards as $guard) {
Log::debug('Checking guard: ' . $guard);
// I am hitting the route for managers in the browser, and I know that a manager user
// is authenticated.
Log::info('Managerial user is: ', ['user' => Auth::guard('manager')->user()] );
if(Auth::guard($guard)->check()) {
Log::debug('Guard: ' . $guard . ' checked');
$this->region_id = Auth::guard($guard)->user()->region_id;
}
}
Log::debug('Region query scope constructor just finished');
}
public function apply(Builder $builder, Model $model)
{
$builder->where('region_id', $this->region_id);
}
}
EDIT:
I am not sure whether it is by design, but there's a thread on Laracasts forums, where someone reports a similar issue. The solution recommended in that thread is to move the middleware from 'web' group to 'global' - I don't really understand whether it is something that should be done or not.
EDIT 2:
Quite interestingly, when I define an anonymous global scope directly in the model using a closure, the authenticated user is accessible. For example:
static::addGlobalScope('region', function (Builder $builder) {
Log::info('Authenticated user is: ', ['user' => Auth::user()] );
});
works fine.
Trying to pass the user value directly to the scope object does not work:
static::addGlobalScope( new RegionScope( Auth::user() ) );
This repo is for bug tracking. Use the forums or slack channel for solving your issue
The scope constructor executes very early, before the auth middleware has run. Resolve the user within the apply method, not in the constructor.
@sisve Thank you. It worked!
Can someone please help me understand the sequence of execution or at least guide me in the right direction.
I had a somewhat related problem
Unable to access authenticated User's Roles and Permissions in global scope
And solved it by delaying the load using lazy loading instead of eager loading a scoped model.
Any help on the sequence front would be appreciated.
Thanks
Most helpful comment
The scope constructor executes very early, before the auth middleware has run. Resolve the user within the apply method, not in the constructor.