laravel最佳实践本质是围绕可维护性、可测试性与团队协作效率的约束与取舍,而非教条式清单;应优先用模型本地作用域处理单表逻辑,复杂场景再引入service,验证行为由accept头驱动,日期统一存utc并在展示层转换,小项目避免过度架构。

直接说结论:Laravel 最佳实践不是一堆“应该做”的清单,而是围绕可维护性、可测试性和团队协作效率形成的一套约束与取舍。真正踩过坑的人会发现,多数所谓“最佳”其实是在特定规模和团队成熟度下才成立的。
胖模型 + 本地作用域比“Service/Repository 模式”更轻量实用
很多项目一上来就建 app/Services 和 app/Repositories,结果 80% 的业务逻辑只涉及单表增删改查,反而让调用链变长、IDE 跳转困难、测试成本上升。
更实际的做法是:
- 把常用查询条件封装成模型的本地作用域(
scopePublished()、scopeWhereTitle()),支持链式调用,语义清晰 - 复杂多表关联或跨服务逻辑再下沉到 Service 类,而不是一概而论
- 避免在 Repository 中写业务规则(比如“用户注册后发邮件”),那是 Service 或 Action 的职责
- 作用域方法必须返回
$query,且命名去掉scope前缀后要能读通(published()比isPublished()更符合 Laravel 习惯)
$request->validate() 必须配合 Accept: application/json
API 验证失败却跳 302 重定向?不是代码写错了,是客户端没发对请求头。
Laravel 的 $request->validate() 行为完全由 Accept 请求头驱动:
- 带
Accept: application/json→ 抛ValidationException→ 自动返回 JSON + HTTP 422 - 不带或为
text/html→ 重定向回表单页(Web 场景默认行为) - 前端用 Axios/Fetch 时务必显式设置:
headers: { Accept: 'application/json' } - Postman 测试 API 时,手动在 Headers 标签页加这一行,否则永远看不到预期错误结构
日期一律存 UTC,转换只发生在展示层
数据库里存 2026-04-24 19:30:00 这种“看起来对”的时间,上线跨国用户后准炸。
正确姿势是:
- 数据库字段类型用
datetime(非timestamp),PHP 层统一用 Carbon 处理 - 所有入库前的时间都调用
$carbon->setTimezone('UTC')->toDateTimeString() - Blade 中显示给用户时,用
{{ $post->created_at->timezone($user->timezone)->format('Y-m-d H:i') }} - 别在模型的
getCreatedAtAttribute()里自动转时区——这会让 API 返回值不可预测,也破坏了 Eloquent 的序列化一致性
Artisan 命令别手写重复逻辑,优先复用 Eloquent + Collection
写个 php artisan sync:users 却手动拼 SQL、用 DB::insert() 批量插入?既难测又难调。
更稳的路径是:
- 用
User::chunkById(500, function ($users) { ... })分批处理,避免内存溢出 - 数据转换用 Collection 方法(
map()、filter()、pluck()),不写 for 循环 - 批量更新优先
upsert()(Laravel 9.2+),而不是先查再判断是否存在 - 命令类里别塞太多业务逻辑,抽成独立 Service 类,方便单元测试和后续 Web 端复用
最常被忽略的其实是“何时不适用最佳实践”——比如小团队维护的内部工具,硬套模块化目录或完整测试套件,反而拖慢迭代。真正的最佳实践,是清楚知道每个选择背后 trade-off 是什么,而不是复制粘贴别人项目的结构。











