You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中withOnly方法未限制关联查询的问题排查

问题分析与解决

问题描述

我定义了User模型,通过$with设置了默认预加载subscription和photo关联:

class User extends Authenticatable
{
    /* ........ */

    protected $with = ['subscription', 'photo'];

    public function subscription(): HasOne
    {
        return $this->hasOne(Subscriptions::class, 'id', 'subscription_id');
    }
    
 
    public function photo(): HasOne
    {
        return $this->hasOne(Files::class, 'id', 'photo_file_id');
    }
}

在查询Notifications模型时,我想只加载fromuser关联,不加载User的默认关联,于是用了withOnly方法:

$notifications = \App\Models\Notifications::withOnly('fromuser')
    ->where('to_user_id', auth()->user()->id)
    ->limit(15)
    ->orderBy('id', 'DESC')
    ->get();

但Debug Bar显示User的subscription和photo还是被加载了,Notifications模型代码如下:

class Notifications extends Model
{
    /* ..... */
    
    public function fromuser(): HasOne
    {
        return $this->hasOne(User::class, 'id', 'from_user_id');
    }

    public function touser(): HasOne
    {
        return $this->hasOne(User::class, 'id', 'to_user_id');
    }
}

我不想每次用User关联都写闭包排除默认关联,请问问题出在哪?

原因与解决方法

问题根源

withOnly('fromuser')的作用是覆盖Notifications模型自身的默认预加载配置(如果Notifications有$with属性的话),但它管不到关联的User模型自带的$with预加载规则。只要User模型被加载,它就会自动触发自己的默认关联预加载,和Notifications的查询配置无关。

解决方法

下面两种方法可以不用每次写重复闭包,还能适配后续新增的默认关联:

方法1:在User模型定义全局作用域

在User模型里加一个作用域方法,专门用来排除所有默认预加载:

class User extends Authenticatable
{
    // ...原有代码

    public function scopeWithoutDefaultRelations($query)
    {
        // 直接用模型的$with属性来排除,后续新增默认关联会自动生效
        return $query->without($this->with);
    }
}

之后查询时,只需要在关联里调用这个作用域:

$notifications = Notifications::with(['fromuser' => function($query) {
    $query->withoutDefaultRelations();
}])
    ->where('to_user_id', auth()->id())
    ->limit(15)
    ->orderByDesc('id')
    ->get();

方法2:使用Laravel自带的withoutGlobalEagerLoads方法

Laravel提供了withoutGlobalEagerLoads方法,可以直接禁用模型的所有默认预加载,不用自己写作用域:

$notifications = Notifications::with(['fromuser' => function($query) {
    $query->withoutGlobalEagerLoads();
}])
    ->where('to_user_id', auth()->id())
    ->limit(15)
    ->orderByDesc('id')
    ->get();

这个方法会完全取消User模型的$with预加载,不管里面有什么关联,适合需要彻底禁用默认预加载的场景。

内容的提问来源于stack exchange,提问作者Linesofcode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 16:52:50