laravel 11 中 with() 未解决 n+1 问题,主因是预加载被绕过或配置错误,需检查执行时机(with 必须在 get/paginate 前)、select 字段完整性(必须包含外键和主键)、关系名严格匹配(大小写、单复数、拼写)、并用查询日志或 telescope 验证 sql 是否生成 in 子查询。

如果在 Laravel 11 中已使用 with() 却仍出现 N+1 查询,说明预加载未真正生效——不是 Laravel 失效了,而是预加载逻辑被绕过、覆盖或配置错误。排查需从执行时机、字段完整性、关系定义和运行环境四方面入手。
确认 with() 是否真的在 get() 之前调用
with() 是查询构造器方法,必须出现在最终执行查询的方法(如 get()、first()、paginate())之前。任何链式写法中把它放在后面,都会被忽略:
- ❌ 错误:
Post::all()->with('user')——all()已执行查询并返回 Collection,with()无效 - ❌ 错误:
Post::where('status', 'draft')->with('user')->paginate(15)->withQueryString()——paginate()是终止方法,with()必须在其前 - ✅ 正确:
Post::with('user')->where('status', 'draft')->paginate(15)
检查 select() 是否漏掉外键或主键
Eloquent 依赖外键(如 user_id)和关联模型主键(如 users.id)完成数据映射。若显式 select() 时遗漏这些字段,预加载的关联数据无法匹配,循环中访问 $post->user 会再次触发懒加载:
- ❌ 错误:
Post::select('id', 'title')->with('user')->get()—— 主查询没选user_id,user关联无法绑定 - ❌ 错误:
Post::with(['user' => fn ($q) => $q->select('name', 'email')])->get()—— 关联查询缺id,映射失败 - ✅ 正确:
Post::select('id', 'title', 'user_id')->with(['user' => fn ($q) => $q->select('id', 'name', 'email')])->get()
验证关系方法名与模型定义是否完全一致
关系名大小写、复数/单数、拼写必须与模型中定义的函数名严格一致。Eloquent 不报错,但会静默跳过预加载:
- 模型中定义的是
public function author() { return $this->belongsTo(User::class); } - ❌ 错误写法:
with('user')或with('Author')—— 名称不匹配,预加载不触发 - ✅ 正确写法:
with('author') - 嵌套关系同理:
with('author.profile')要求模型有author()和Profile模型中有profile()方法
开启查询日志或使用 Telescope 确认实际 SQL
不要依赖“写了 with 就一定生效”,直接看数据库到底执行了什么:
- 临时在代码开头加:
DB::enableQueryLog();,之后dd(DB::getQueryLog());查看是否生成了预加载对应的IN子查询或JOIN - 安装 Laravel Telescope,打开 /telescope 页面,在请求详情里查看完整 SQL 列表
- 典型成功预加载日志包含两段:一段查
posts,一段查users并带WHERE id IN (1,2,3...);若只看到多次SELECT * FROM users WHERE id = ?,说明懒加载仍在运行











