laravel 中应使用 formrequest 抽离验证逻辑,替代控制器内 $request->validate();需正确实现 authorize()、messages()、rules(),配合 rule::unique()、validated()、自定义 rule 类及 prepareforvalidation() 提升复用性、可测性与安全性。

用 Laravel 的 FormRequest 抽离验证逻辑,别在控制器里写 validate()
控制器里堆 $request->validate([...]) 看似简单,但复用难、测试难、错误消息难统一。Laravel 的 FormRequest 类天然适配表单场景,把规则、消息、授权逻辑全收进去,控制器只剩一行 store(StorePostRequest $request)。
常见踩坑点:
- 忘记运行
php artisan make:request StorePostRequest后手动启用——必须在类里把authorize()返回true或写清权限判断,否则请求直接 403 - 自定义错误消息写在
messages()方法里,但字段名要和规则键完全一致,比如规则是'title.required',消息键就得是'title.required',不是'title.required_string' - 需要动态规则(如“编辑时邮箱可不填,新增时必填”)?在
rules()方法里用$this->route('id') !== null判断上下文,别硬编码 ID
用 Rule::unique() 避免手写 DB 查询验证唯一性
“用户名不能重复”这种需求,很多人还写 DB::table('users')->where('name', $name)->exists() 再抛异常。这既绕过验证器流程,又难做批量插入兼容。
正确姿势是直接用 Illuminate\Validation\Rule:
立即学习“PHP免费学习笔记(深入)”;
use Illuminate\Validation\Rule;
return [
'email' => [
'required',
'email',
Rule::unique('users')->ignore($this->user ?? null),
],
];
注意点:
-
ignore()第二个参数默认是主键字段名,如果模型主键不是id(比如叫user_id),得显式传:ignore($this->user?->id, 'user_id') - 跨表唯一校验(如检查邮箱是否在
admins表中存在),用Rule::unique('admins', 'email') - 软删除模型要加
->whereNull('deleted_at'),否则已软删记录仍被判定为“存在”
批量赋值前先验证,别等 create() 报错才处理
写 User::create($request->all()) 是危险操作——没过滤字段,没走验证,数据库报错才暴露问题。Laravel 的 validated() 方法就是干这个的:
✅ 正确:User::create($request->validated());
❌ 危险:User::create($request->all()); 或 User::create($request->only(['name', 'email']))(漏字段、无验证)
关键细节:
-
$request->validated()只返回通过验证的字段,自动过滤掉未定义在规则里的键,比only()更安全 - 如果规则里用了
nullable但前端传了空字符串,validated()仍会包含该字段(值为''),需额外用filter()或在prepareForValidation()中清洗 - 配合
withValidator()可在验证后追加逻辑,比如对密码字段自动哈希:$validator->after(...)
自定义验证规则别写闭包,用 php artisan make:rule 生成类
临时写 ['phone' => ['required', function ($attribute, $value, $fail) { ... }]] 看似快,但无法复用、无法测试、IDE 不提示、错误堆栈难定位。
生成类后,规则就变成可导入、可单元测试、可文档化的组件:
php artisan make:rule PhoneWithoutCountryCode
然后在 passes() 里写逻辑,message() 返回提示。使用时直接 PhoneWithoutCountryCode::class。
容易忽略的点:
- 规则类构造函数接收参数(如地区码),要在
make:rule命令后加--invokable才支持new PhoneWithoutCountryCode('CN')调用方式 - 如果规则依赖容器服务(比如要查 Redis),不要在
passes()里直接 new 实例,改用app(Service::class)或注入到构造函数 - 规则类命名必须符合 PSR-4,且文件名首字母大写,否则
Rule::extends()注册时可能找不到类
FormRequest 的 prepareForValidation() 方法——它能在验证前统一 trim、转小写、替换空格,比在每个规则里写 'email' => 'required|email|lowercase|trim'(Laravel 10+ 才有 lowercase)更可控。










