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

Posted

技术标签:

【中文标题】Laravel 5.8:如何在 Global Scope 的 apply 方法中解析 auth()->user()?【英文标题】:Laravel 5.8: How to resolve auth()->user() inside Global Scope's apply method? 【发布时间】:2019-10-22 13:40:47 【问题描述】:

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

我可以轻松处理 User 模型(当前登录的用户),但不能在范围的 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()-&gt;make() 调用 IOC 容器,但是然后呢?

感谢任何提示。

编辑:我想我知道是什么导致了无限循环 (https://github.com/laravel/framework/issues/26113),但我仍然需要找到获取用户的最佳方法……

【问题讨论】:

你试过 Auth::user() 吗?如果您可以将请求放入范围,您也可以尝试 $request->user() 。不知道是什么导致了这种情况,但也许在后台存在冲突,使用解决方法可以解决问题。我不确定,但我认为 Auth::user() 每次使用时都会对数据库进行查询。 【参考方案1】:

简介

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

【讨论】:

【参考方案2】:

我解决了这个问题,只需在成功登录后将用户 ID 添加到会话(Controllers/Auth/LoginController.php,attemptLogin()):

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

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

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

【讨论】:

感谢您的反馈迈克。利用会话来解决我的问题是我最初的计划,但在这种情况下我根本不想依赖会话,因为它不会让我觉得这是解决问题的可靠解决方案......无论如何,谢谢。 【参考方案3】:

您正在寻找的方法正是 Auth::hasUser()。该方法是通过我的 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

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

只需致电Auth::hasUser() 即可防止Auth::user() 引起副作用。

【讨论】:

这太棒了,我正要将此逻辑移至控制器,但坚持认为“laravel 必须对此有答案”。【参考方案4】:

这里的问题是您正在为 User 定义一个范围,在使用 Auth::user() 检索用户时,它将在第一个方法调用被解析之前再次使用该范围,并且我们有一个 ***。我认为有一个聪明的方法,覆盖守卫,并将用户模型保存在可以从那里获取的类中,但我也不喜欢使用内置的 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 ***
    $user = DB::table('users')->where('id', Auth::id())->get()->first();

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

【讨论】:

我不经常写 db::table 逻辑,它可能有点偏离,但我想你明白了。 你所说的对我来说绝对有意义,不幸的是 Auth::id() 也会抛出“达到 '512' 的最大函数嵌套级别,正在中止!”即使在 dd() 中。我曾考虑将范围的数据放入会话变量中,但我不相信这会是多么可靠。 我会在某个时候在我的本地环境中复制它,并在适当的时候找出解决方案。 谢谢马丁,如果你发现了什么,请告诉我。现在作为一种解决方法,我使用了会话,但我认为这个解决方案不是一个稳定的解决方案。 :(

以上是关于Laravel 5.8:如何在 Global Scope 的 apply 方法中解析 auth()->user()?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel 5.8 中附加授权标头

如何在 Laravel 5.8 中使 chart.js 动画/增长/活着?

如何在 laravel 5.8 中将变量传递给路由?

如何在 laravel 5.8 中使用 mailchimp 发送简单邮件

Laravel 5.8 如何获得工作 ID?

如何在 Laravel 5.8 中使用 Eloquent 进行查询?