Laravel - How to use Auth user inside the global scope - php

I want to use Auth user in the global scope
User.php
public function scopeActive($query)
{
return $query->where('accessed_id', auth()->user()->accessed_id);
}
protected static function boot()
{
parent::boot();
static::addGlobalScope('customer_user', function (Builder $builder) {
if(auth()->check()){ // check user is auth or not
$builder->active();
}
});
}
I am unable to get auth user in scope because the scope was early called then after authentication, It's possible to get auth user in the global scope?

Related

Laravel Action - can't bind model to route

While using Laravel Actions Package the model is not binding and returning an empty array.
What am I missing?
namespace App\Actions\User;
use App\Models\User;
use Illuminate\Routing\Router;
use Lorisleiva\Actions\Concerns\AsAction;
class GetUserAction
{
use AsAction;
public static function routes(Router $router)
{
$router->get('users/{user}', static::class);
}
public function handle(User $user): User
{
return $user;
}
RouteServiceProvider
public function boot()
{
Actions::registerRoutes();
}
If I run
public function handle(ActionRequest $request, User $user): User
{
dd($request->route()->parameters());
}
on the handler it does return the id parameter.
I'd like to keep the declaration of the route inside the Action as suggested in the documentation
If I declare it in the routes file it works
Route::get('/users/{user}', GetUserAction::class);

Laravel class property not returning in response

I'm creating an API for a blog using Laravel and in my UserController, for some routes, I get the currently authenticated user as seen below:
public function getUser() {
$user = auth()->user();
return response()->json($user, 200);
}
This returns a json object of the user. I get the user in other functions in the same way. I wanted to move this line into a wider scope so I could access it in all functions so I placed it in the constructor:
class UserController extends Controller {
protected $user;
public function __construct() {
$this->user = auth()->user();
}
}
I refactored getUser to look like this;
public function getUser() {
return response()->json($this->user, 200);
}
Now it just returns an empty object and the 200 status code. Why does making it a class property no longer return the object?
The session middleware has not ran yet; the Controller is instantiated before the Request goes through the middleware stack, so you won't have access to session based authentication at that point (so auth()->user() would be null). You can use a Controller middleware to do this though:
class UserController extends Controller
{
protected $user;
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = $request->user();
return $next($request);
});
}
}
This middleware will run after the other middleware in the stack so you will have access to the session at that point.

Authorization gate not called during service provider

I have an admin gate defined in my AuthServiceProvider that is used to add global query scopes to some models. Suppose I had models A, that is observed by an Observer (registered in AppServiceProvider), and B, that makes use of the admin gate to add global query scopes.
// app/Providers/AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
public function boot()
{
Gate::define('admin', [static::class, 'admin']);
}
public static function admin(User $user): bool
{
return $user->group->name === 'Admin';
}
}
// app/B.php
class B extends Eloquent
{
public static function boot()
{
parent::boot();
if (!Gate::allows('admin')) {
static::addGlobalScope('public', function ($query) {
$query->where('public', true);
});
}
}
}
Up to this point everything worked fine. Then I added a model C that has an Observer and uses the admin gate. As C::observe() fires C::boot() and the AppServiceProvider is registered before the AuthServiceProvider the gate was not defined and I extracted the Observer registration to a new ObserverServiceProvider that is registered after AuthServiceProvider.
// app/C.php
class C extends Eloquent
{
public static function boot()
{
parent::boot();
if (!Gate::allows('admin')) {
static::addGlobalScope('public', function ($query) {
$query->where('public', true);
});
}
}
}
// app/Providers/ObserverServiceProvider.php
class ObserverServiceProvider extends ServiceProvider
{
public function boot()
{
A::observe(AObserver::class);
C::observe(CObserver::class);
}
}
// config/app.php
'providers' => [
//...
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
//...
App\Providers\ObserverServiceProvider::class,
]
My problem:
The observers for A and C are still working, as well as the admin gate in B's boot() method, but Gate::allows('admin') in C always returns false without even calling the gate function.
Adding a var_dump(Gate::has('admin')) in C::boot() outputs true and using #can('admin') later in the View during the same request works fine as well, so the gate is definitely defined and working in principle.
The authorization gate cannot be called as the session data (and therefore the authenticated user) is made available by the StartSession middleware, which runs after the service providers.
The problem can be solved by putting the Gate::allows() check inside the anonymous function, so it is only executed when building a query:
// app/C.php
class C extends Eloquent
{
public static function boot()
{
parent::boot();
static::addGlobalScope('public', function ($query) {
if (!Gate::allows('admin')) {
$query->where('public', true);
}
});
}
}

Laravel - Access The Authenticated User In A Model Observer

I followed the documentation for creating a model observer here https://laravel.com/docs/5.5/eloquent#observers.
But when I try and access the authenticated user I get null.
How can I access the authenticated user in the model observer?
<?php
namespace App\Observers;
use App\Customer;
class CustomerObserver
{
public function created(Customer $customer)
{
dd(auth()->user());
}
public function updated(Customer $customer)
{
dd(auth()->user());
}
}
I've also tried this inside the Customer model and it returns null as well.
public static function boot()
{
parent::boot();
self::updated(function ($model) {
dd(auth()->user());
});
}
Ok, so stupid mistake on my end.
Thanks #fubar for the tip.
I was using a custom authentication provider so I needed to do this:
dd(auth()->guard('admin')->user());

Laravel global scope on retrieving a model

I'm retrieving Events from the database and every time I get events from the database I want to check where active_from <= today.
How am I able to define a global scope which will be used when I retrieve a model?
You need to create a trait like EventsTrait and inside that trait, add a bootEventTrait function that will add a global scope like EventsScope. Use SoftDeletingTrait as pattern.
EventTrait
trait EventsTrait {
public static function bootEventsTrait()
{
static::addGlobalScope(new EventsScope);
}
}
EventScope
use Illuminate\Database\Eloquent\ScopeInterface;
use Illuminate\Database\Eloquent\Builder;
class EventScope implements ScopeInterface {
public function apply(Builder $builder)
{
$builder->where("active_from","<=", "today");
}
public function remove(Builder $builder)
{
// remove scope
}
}
You can add a global scope (eg: is_active) to the Event model.
// App\Event.php
use Carbon\Carbon;
public static function boot(){
parent::boot();
static::addGlobalScope('is_active', function($builder){
$builder->where('active_from', '<=', Carbon::now());
})
}

Categories