Laravel 5.8:如何在Global Scope的apply方法中解析auth()-> user()?

时间:2019-06-07 03:45:23

标签: php laravel inversion-of-control ioc-container laravel-5.8

仅当经过身份验证的用户角色等同于某些角色时,才需要应用特定的全局范围。这样具有特定角色的用户将只能对给定的记录子集执行查询。

我可以轻松处理(当前登录用户的)用户模型,但不能在范围的apply方法中处理。

  

https://github.com/laravel/framework/issues/22316#issuecomment-349548374

     

作用域构造函数在auth中间件之前很早就执行   已经运行。在apply方法而不是构造函数中解析用户。

好的,所以我在apply方法中:

<?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');
    }
}

很显然,第二dd给出:

  

达到最大功能嵌套级别“ 512”,正在中止!

该如何解决?我想我应该通过app()->make()调用IOC容器,但是然后呢?

感谢任何提示。

edit:我想我看到了造成无限循环(https://github.com/laravel/framework/issues/26113)的原因,但我仍然需要找到一种最佳方法来获取User…

4 个答案:

答案 0 :(得分:2)

简介

终于找到了解决方案,我一直在尝试修复它并寻找解决方案一个多小时。我所拥有的问题是我正在使用特征将全局范围应用于某些模型而不是所有模型。列表很大,所以我必须找到一种在全局范围内使用 auth()->check() 的方法。

解决方案

因此,在模型类正在使用的模型类或特征中,您可以侦听 RouteMatched 事件,这确保会话提供程序已启动。然后在事件侦听器的闭包/可调用中添加全局范围。

代码

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
        });
    });
}

答案 1 :(得分:0)

这里的问题是您正在为User定义一个作用域,同时使用Auth :: user()检索用户时,它将在解决第一个方法调用之前再次利用该作用域,因此我们有了StackOverflow。我认为这是一种聪明的方法,可以覆盖防护措施,并将用户模型保存在可以从那里获取的类上,但是也不喜欢使用内置的Laravel功能进行调整。

一种简单的方法可能是利用Auth :: id并通过DB :: table获取您所需的信息,虽然它不是很漂亮,但我认为这是解决问题的直接方法。

public function apply(Builder $builder, Model $model)
{
    // this will not trigger the scope when you fetch it out again, and avoiding the stackoverflow
    $user = DB::table('users')->where('id', Auth::id())->get()->first();

    $builder->where('foo', '=', $user['the_answer_to_life']);
}

答案 2 :(得分:0)

您要查找的方法正是 sentence 。该方法是通过我的PR在Laravel 5.6中添加的。

[5.6] Add ability to determine if the current user is ALREADY authenticated without triggering side effects by mpyw · Pull Request #24518 · laravel/framework

>>> combs = [' '.join(sent) for sent in permutations(sentence.split())]
>>> combs.remove(sentence)
>>> combs
['password successful reset',
 'reset password successful',
 'reset successful password',
 'successful password reset',
 'successful reset password']

只需调用Auth::hasUser()即可防止<?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'); } } } 引起副作用。

答案 3 :(得分:0)

我通过在成功登录后立即将用户ID添加到会话中来解决该问题(Controllers / Auth / LoginController.php,tryLogin()):

$request->session()->put('id_user', $user->id);

...然后在全局范围内获取它,例如:

$id_user = (request()->session()->get('id_user')) ?? null;