循环中访问模型关系会导致n+1查询问题,应使用with()预加载;深层关联需谨慎,限制字段用select();blade中避免$loop->last等触发全量加载的操作;api返回前需过滤字段,配置和路由缓存必须开启。

循环里别调模型关系,否则就是N+1
接口响应慢,十次有八次是循环里写了 $user->posts 或 $item->category->name 这类访问,又没预加载。Laravel 会为每一项单独查一次数据库,100 条数据就发 100 条 SQL —— 不是 PHP 慢,是数据库被反复锤。
- 控制器里必须用
with()预加载:User::with('posts', 'profile')->get() - 嵌套两层以上别写
with('posts.comments.author'),先确认是否真需要;深层关联容易拖垮内存 - 如果只读个别字段,加
select()限制:with(['posts' => function ($q) { $q->select('id', 'title', 'user_id'); }]) - 模板里出现
{{ $user->posts->count() }}?立刻检查是否漏了预加载——Debugbar 的 Queries 面板里看到一串重复 SQL 就是它
别在 Blade 循环里用 $loop->last 或 $loop->remaining
这些属性会让 Laravel 强制把整个 Collection 加载进内存再算长度,上千条数据时直接卡住渲染,比 N+1 还隐蔽。
-
$loop->index和$loop->iteration是安全的,不依赖集合大小 - 要判断“最后一项”,用原生 PHP:
@if($index === count($items) - 1) - 分页结果(
$users->paginate())里$loop基本不可靠,别信 - Blade 里写
{{ $item->relation->name }}却没预加载?等于悄悄触发 N+1,Debugbar 都不一定报错
API 返回别直接 return $collection,先过 Resource 或 only()
直接 return User::get() 看似省事,但 Eloquent 会递归展开所有已加载关联、执行访问器、转日期格式——哪怕你前端只用 id 和 name。
- 用
->map->only(['id', 'name', 'email'])替代全量 toArray() - ApiResource 类里别在
toArray()里写$this->posts,改用$this->whenLoaded('posts', ...) - Resource 构造时就执行
toArray(),所以预加载必须在查询阶段完成,不是 Resource 里补 - 大数组返回前,先
dd($data)看结构:有没有意外加载的关联?有没有未过滤的 content 字段?
config:cache 和 route:cache 不开,其他优化都打七折
没跑过 php artisan config:cache 和 php artisan route:cache,Laravel 每次请求都要重解析全部配置和路由文件,白送 5–15ms TTFB 延迟,尤其在高并发下明显。
-
route:cache不支持闭包路由,API 路由用Route::apiResource()是安全的 -
config:cache会忽略.env里没在 config 文件中显式调用的变量,比如写了APP_DEBUG=true但config/app.php没写env('APP_DEBUG'),这个值就不会进缓存 - 改了
.env或配置文件后,必须手动清 + 重生成,不能只清不重生成 - 生产环境别频繁跑
config:clear,那是自造 CPU 尖刺
真实瓶颈往往不在“怎么写更快”,而在“哪一行偷偷多查了一次库”或“哪个配置没固化”。盯着 Debugbar 的 Queries 和 Timeline 面板,比看文档更管用。











