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

Laravel Eloquent with()与find()配合失效,where()却正常?

Laravel Eloquent with() 关联查询问题:原因分析与修复方案

问题背景

我最近在用Laravel Eloquent的with()方法做三张表的关联查询,模型之间的关联已经配置好了,但遇到了一个让人困惑的问题:

我最初写了这段代码:

$request_form = RequestForm::find(7)->with(['RequestFormsCheck' => function ($q) { 
    $q->orderBy("created_at", "DESC")->with('RequestFormsCheckOptions'); 
}])->get();
dd($request_form);

结果返回的是整个request_forms表的所有数据,完全不是我要的id=7的那条记录;后来我把get()换成first(),结果返回的记录id居然是1,完全不符合预期。

但换成下面这段代码就正常了,能正确获取到id=7的记录及其关联数据:

$request_form = RequestForm::where('id', 7)->with(['RequestFormsCheck' => function ($q) { 
    $q->orderBy("created_at", "DESC")->with('RequestFormsCheckOptions'); 
}])->first();
dd($request_form->toArray());

附上我的数据表迁移代码:

request_forms 表迁移

Schema::create('request_forms', function (Blueprint $table) { 
    $table->increments('id'); 
    $table->string('name', 100)->nullable(); 
    $table->string('email', 100)->nullable(); 
    $table->string('telephone', 20)->nullable(); 
    $table->text('description')->nullable(); 
    $table->string('site', 100); 
    $table->integer('item_id'); 
    $table->string('lang'); 
    $table->timestamps(); 
});

request_forms_check_options 表迁移

Schema::create('request_forms_check_options', function (Blueprint $table) { 
    $table->increments('id'); 
    $table->string('title', 500); 
    $table->timestamps(); 
});

request_forms_checks 表迁移

Schema::create('request_forms_checks', function (Blueprint $table) { 
    $table->increments('id'); 
    $table->integer('request_form_id')->unsigned(); 
    $table->foreign('request_form_id')->references('id')->on('request_forms')->onDelete('cascade'); 
    $table->tinyInteger('type')->comment("1: option, 2:description"); 
    $table->integer('request_forms_check_options_id')->unsigned()->nullable(); 
    $table->foreign('request_forms_check_options_id')->references('id')->on('request_forms_check_options')->onDelete('cascade'); 
    $table->text('description')->nullable(); 
    $table->timestamps(); 
});

为什么会出现这种差异?

核心问题就是方法调用顺序错了!

当你执行RequestForm::find(7)时,Laravel已经立刻执行了数据库查询,返回的是一个RequestForm模型实例(不是查询构造器)。这时候你再调用with()方法,它的作用是给这个模型实例预加载关联,但随后你调用get()方法,会直接创建一个全新的查询构造器,执行SELECT * FROM request_forms,所以返回了全表数据。

同理,把get()换成first()时,也是发起了全新的查询,默认取表中第一条记录(也就是id=1的那条),自然和你要的id=7无关。

而where('id',7)->with()->first()的写法是正确的:先通过where()给查询构造器添加条件,再用with()指定要预加载的关联,最后first()执行查询——整个过程都是基于同一个查询构造器,所以能精准获取到id=7的记录及其关联数据。


修复方案

你有两种靠谱的写法可以选择:

方案一:先指定预加载,再查询(推荐)

把with()放在find()前面,这样查询构造器会先记住要预加载的关联,再执行查询获取指定id的记录:

$request_form = RequestForm::with(['RequestFormsCheck' => function ($q) { 
    $q->orderBy("created_at", "DESC")->with('RequestFormsCheckOptions'); 
}])->find(7);
dd($request_form->toArray());

或者用你已经验证过的where()写法,效果是一样的。

方案二:延迟加载关联(适合已获取模型实例的场景)

如果已经拿到了模型实例(比如之前已经查询过这条记录),可以用load()方法来延迟加载关联,而不是with():

// 先获取id=7的模型实例
$request_form = RequestForm::find(7);
// 给这个实例加载关联数据
$request_form->load(['RequestFormsCheck' => function ($q) { 
    $q->orderBy("created_at", "DESC")->with('RequestFormsCheckOptions'); 
}]);
dd($request_form->toArray());

load()是针对已存在的模型实例加载关联,不会重新发起全表查询,完美解决你的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:11:04