
本文介绍如何在 Laravel 中通过 with() 方法高效预加载同一模型的多个嵌套关联关系,避免 N+1 查询问题,例如同时加载 Product 的 price 和 masterlist 关系。
本文介绍如何在 laravel 中通过 `with()` 方法高效预加载同一模型的多个嵌套关联关系,避免 n+1 查询问题,例如同时加载 `product` 的 `price` 和 `masterlist` 关系。
在 Laravel 的 Eloquent ORM 中,with() 方法是实现“懒加载优化”(即预加载)的核心工具。当需要从主模型(如 StoreStock)出发,同时获取其关联模型(如 Product)的多个子级关系(如 price 和 masterlist)时,关键在于正确传递关系路径数组,而非链式调用或多次 with()。
错误写法(会导致语法错误或仅生效最后一个):
// ❌ 错误:all() 后不能链式调用 with();且 get() 重复调用
StoreStock::all()->with('product.price')->get();
// ❌ 错误:连续 with() 会覆盖前一个,最终只加载 price
StoreStock::with('product.price')->with('product.masterlist')->get();✅ 正确做法:将多个嵌套关系路径以字符串数组形式传入单次 with():
$storeStocks = StoreStock::with([
'product.price',
'product.masterlist'
])->get();该写法会一次性触发 3 次查询(而非 N+1):
- 1 次查询 store_stocks 主表;
- 1 次通过 product_id 批量查询关联的 products;
- 1 次通过 product_id 批量查询所有匹配的 prices 和 masterlists(Laravel 自动合并关联键)。
? 进阶提示:
- 若需条件约束某关系(如只加载有效的价格),可结合闭包:
StoreStock::with(['product.price' => function ($query) { $query->where('status', 'active'); }, 'product.masterlist'])->get(); - 支持无限层级嵌套,如 'product.category.parent';
- 推荐始终在 get() 前调用 with(),避免先取集合再预加载(Collection::load() 仅适用于已存在的模型实例)。
⚠️ 注意事项:
- 关系名称必须与模型中定义的 Eloquent 关联方法名完全一致(区分大小写);
- 确保数据库外键存在且索引合理(如 products.id、prices.product_id),否则预加载性能不升反降;
- 使用 dd($storeStocks) 或 Laravel Telescope 验证实际 SQL,确认是否真正避免了 N+1。
通过这种结构化预加载,你不仅能显著提升接口响应速度,还能让数据结构更清晰——每个 StoreStock 实例均可安全访问 $stock->product->price 和 $stock->product->masterlist,无需运行时触发额外查询。










