I need to apply specific global scope only if authenticated user's role is equal to something. So that user with certain role will only be able to execute queries on a given subset of records.
I can easily deal with User model (of currently logged in user) but not inside scope's apply method.
https://github.com/laravel/framework/issues/22316#issuecomment-349548374
The scope constructor executes very early, before the auth middleware has run. Resolve the user within the apply method, not in the constructor.
OK, so I'm inside the apply
method:
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class UserScopeForSalesContactAdmin implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
dd('within apply', auth()->user());
dd(auth()->user()); // won't work
$builder->where('foo', '=', 'something from currently logged in user');
}
}
Obviously second dd
gives:
Maximum function nesting level of '512' reached, aborting!
How to resolve this? I imagine I should call IOC container via app()->make()
but then?
Thanks for any hints.
edit: I think I see what's causing the infinite loop (https://github.com/laravel/framework/issues/26113) but still I need to find a best way to obtain the User…
Laravel includes built-in authentication and session services which are typically accessed via the Auth and Session facades. These features provide cookie-based authentication for requests that are initiated from web browsers. They provide methods that allow you to verify a user's credentials and authenticate the user.
Auth::check() defers to Auth::user() . It's been that way since as long as I can remember. In other words, Auth::check() calls Auth::user() , gets the result from it, and then checks to see if the user exists. The main difference is that it checks if the user is null for you so that you get a boolean value.
The method what you are looking for is exactly Auth::hasUser()
. The method was added in Laravel 5.6 through my PR.
[5.6] Add ability to determine if the current user is ALREADY authenticated without triggering side effects by mpyw · Pull Request #24518 · laravel/framework
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
class UserScopeForSalesContactAdmin implements Scope
{
/**
* Apply the scope to a given Eloquent query builder.
*
* @param \Illuminate\Database\Eloquent\Builder $builder
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public function apply(Builder $builder, Model $model)
{
if (Auth::hasUser() && Auth::user()->role === 'something') {
$builder->where('foo', '=', 'something from currently logged in user');
}
}
}
Just call Auth::hasUser()
to prevent Auth::user()
from causing side effects.
Introduction
Finally found a solution for this, I have been trying to fix it and looking for solutions for over an hour. The problem with what I have is that I am using a trait to apply the global scope on certain models and not all models. The list is big so I had to find a way to use auth()->check()
in a global scope.
Solution
So in your model class or trait that is being used by your model class, you can listen to the RouteMatched
event, which assures that the session provider is booted. Then add the global scope within the the event listener's closure/callable.
Code
use Illuminate\Routing\Events\RouteMatched;
/**
* The "boot" method of the model.
*
* @return void
*/
protected static function boot()
{
Event::listen(RouteMatched::class, function () {
static::addGlobalScope('company', function ($query) {
// auth()->check() will now return the correct value
// Logic here
});
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With