Laravel全局作用域导致User关联查询冗余,如何规避?
解决方法
方法一:利用认证事件自动加载关联(推荐)
移除User模型中的全局作用域,转而在用户认证成功后自动加载所需关联。这种方式只会给当前认证用户加载关联,不会影响其他User查询。
- 在
app/Providers/AuthServiceProvider.php的boot方法中添加事件监听:
use Illuminate\Auth\Events\Authenticated; use Illuminate\Support\Facades\Event; public function boot() { $this->registerPolicies(); Event::listen(Authenticated::class, function ($event) { // 加载需要的关联 $event->user->load('subscription.lastpayment', 'tickets'); }); }
- 删除User模型中对应的全局作用域代码。
之后依然可以直接访问auth()->user()->subscription、auth()->user()->tickets,而关联查询(如tickets->user)不会加载额外的冗余关联。
方法二:临时排除全局作用域
如果不想移除全局作用域,可以在查询Tickets时,针对关联的User查询排除该全局作用域:
$tickets = \App\Models\Tickets::with(['user' => function ($query) { $query->withoutGlobalScope('subscription'); }]) ->with('lastmessage') ->with(['lastmessage.user' => function ($query) { $query->withoutGlobalScope('subscription'); }]) ->where('subscription_id', auth()->user()->subscription_id) ->get();
这种方式适合只在特定查询中规避全局作用域的场景。
方法三:自定义方法获取带关联的认证用户
移除全局作用域,在User模型中添加一个静态方法,专门用于获取带关联的认证用户:
class User extends Authenticatable { // ... 其他代码 public static function getAuthUserWithRelations() { return static::with('subscription.lastpayment', 'tickets') ->findOrFail(auth()->id()); } }
之后在需要的地方用User::getAuthUserWithRelations()代替auth()->user(),其他User查询(包括关联查询)不受影响。
方法四:修改全局作用域,仅对认证用户查询生效
修改全局作用域的逻辑,只在查询当前认证用户时才加载关联:
protected static function booted() { static::addGlobalScope('subscription', function (Builder $builder) { // 仅当当前有认证用户,且查询条件包含当前用户ID时加载关联 if (auth()->check()) { $query = $builder->getQuery(); // 判断查询是否针对当前认证用户 $hasUserIdCondition = collect($query->wheres)->some(function ($where) { return $where['column'] === 'id' && $where['value'] === auth()->id(); }); if ($hasUserIdCondition) { $builder->with('subscription.lastpayment', 'tickets'); } } }); }
这种方式可以保留全局作用域,但仅对查询当前认证用户的请求生效,关联查询的其他User不会触发。
内容的提问来源于stack exchange,提问作者Linesofcode
相关产品推荐
相关产品推荐

