Laravel 模型关联与预加载:用 with/load 解决 N+1 与关联写入
test12026-09-274 次阅读
N+1 问题
当你查出 20 篇文章,再在循环里访问每篇的作者,ORM 会为每篇文章各发一条 SQL 查用户,总共 1+20 条查询。这种 N+1 在列表页尤其致命。Laravel 用预加载把关联一次性取出,把 N+1 压成 2 条查询。
预加载 with
with 在查询时通过 LEFT JOIN 或额外一条 IN 查询,把关联模型一并加载,之后访问 $post->author 不再触发数据库。
<?php
use App\Models\Post;
// 一次性取出文章与作者、评论
$posts = Post::with(['author', 'comments'])
->where('status', 'published')
->latest()
->paginate(20);
foreach ($posts as $post) {
// 不额外查库
echo $post->author->name;
}
延迟预加载 load
当模型集合已经取出,再想补加载关联,用 load 做延迟预加载,避免重新查询主表。
$posts = Post::all();
// 事后补充加载评论
$posts->load('comments');
// 单个模型也能用 load
$post = Post::find(1);
$post->load('author', 'comments');
关联写入与同步
关联不只用来读,还能直接写入。hasMany 的 create 自动填充外键;多对多用 attach/sync 维护中间表,sync 会替换整个集合,diff 的部分自动增删。
// 给文章追加一条评论,自动写入 post_id
$post->comments()->create([
'content' => '写得很清楚',
'user_id' => auth()->id(),
]);
// 给用户设置角色(多对多),sync 会移除未列出的旧角色
$user->roles()->sync([1, 3, 5]);
// 仅新增不删除,用 attach
$user->roles()->attach(2);
带约束的预加载
有时只想要“审核通过的评论”或“最近三篇”,可以在 with 里传闭包加约束,关联数据在 SQL 层就被过滤。
$posts = Post::with(['comments' => function ($query) {
$query->where('approved', true)->latest()->limit(3);
}])->get();
// 只统计数量而不取全量
$posts = Post::withCount('comments')->get();
foreach ($posts as $post) {
echo $post->comments_count;
}
- 列表页一律用 with 预加载关联,消除 N+1。
- 已拿到集合再补关联,用 load 而不是重新查主表。
- 多对多维护用 sync 整体替换,attach 仅增量新增。
T
test1
文章作者
N+1 问题 当你查出 20 篇文章,再在循环里访问每篇的作者,ORM 会为每篇文章各发一条 SQL 查用户,总共 1...
- 分类
- 技术
- 发布时间
- 2026-09-27
- 字数
- 约 1429 字
- 阅读
- 4 次
