在Laravel项目开发中,我们经常会遇到需要针对不同的业务操作对多个字段做选择性验证的场景,比如用户注册时需要验证手机号和密码,而用户修改资料时只需要验证昵称和头像,同时还需要把所有的验证错误一次性返回,方便前端统一展示。这个时候封装一个通用验证类就能很好地解决这个问题。

通用验证类的设计思路
我们的通用验证类需要满足几个核心需求:支持传入场景标识,根据场景加载对应的验证规则;能够收集所有字段的验证错误,而不是遇到第一个错误就停止;可以灵活扩展新的验证场景和规则。整体设计可以分为三个部分:场景与规则的映射配置、验证逻辑执行、错误信息收集。
通用验证类的实现代码
首先我们创建通用验证类App\Services\CommonValidator,代码如下:
<?php
namespace App\Services;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class CommonValidator
{
// 场景对应的验证规则映射
protected static $sceneRules = [
// 用户注册场景
'user_register' => [
'mobile' => 'required|regex:/^1[3-9]\d{9}$/',
'password' => 'required|min:6|max:20',
'confirm_password' => 'required|same:password',
],
// 用户修改资料场景
'user_update' => [
'nickname' => 'required|max:20',
'avatar' => 'sometimes|image|max:2048',
],
// 订单提交场景
'order_submit' => [
'goods_id' => 'required|integer|min:1',
'count' => 'required|integer|min:1|max:100',
'address_id' => 'required|integer|min:1',
],
];
// 场景对应的自定义错误提示
protected static $sceneMessages = [
'user_register' => [
'mobile.required' => '手机号不能为空',
'mobile.regex' => '手机号格式不正确',
'password.required' => '密码不能为空',
'password.min' => '密码长度不能少于6位',
'password.max' => '密码长度不能超过20位',
'confirm_password.required' => '确认密码不能为空',
'confirm_password.same' => '两次输入的密码不一致',
],
'user_update' => [
'nickname.required' => '昵称不能为空',
'nickname.max' => '昵称长度不能超过20位',
'avatar.image' => '头像必须是图片格式',
'avatar.max' => '头像大小不能超过2M',
],
'order_submit' => [
'goods_id.required' => '商品ID不能为空',
'goods_id.integer' => '商品ID必须是整数',
'count.required' => '购买数量不能为空',
'count.integer' => '购买数量必须是整数',
'count.min' => '购买数量不能少于1',
'count.max' => '购买数量不能超过100',
'address_id.required' => '地址ID不能为空',
'address_id.integer' => '地址ID必须是整数',
],
];
/**
* 执行验证并返回所有错误
* @param string $scene 验证场景
* @param array $data 待验证的数据
* @return array 错误信息数组,为空则表示验证通过
*/
public static function validate(string $scene, array $data): array
{
// 判断场景是否存在
if (!isset(static::$sceneRules[$scene])) {
return ['scene' => '不存在的验证场景:' . $scene];
}
$rules = static::$sceneRules[$scene];
$messages = static::$sceneMessages[$scene] ?? [];
// 创建验证器,设置停止验证为false,收集所有错误
$validator = Validator::make($data, $rules, $messages);
$validator->stopOnFirstFailure(false);
// 验证失败返回所有错误
if ($validator->fails()) {
return $validator->errors()->all();
}
return [];
}
}实际使用示例
接下来我们看在不同业务场景中如何使用这个通用验证类,首先以用户注册为例:
<?php
namespace App\Http\Controllers;
use App\Services\CommonValidator;
use Illuminate\Http\JsonResponse;
class UserController extends Controller
{
// 用户注册接口
public function register(): JsonResponse
{
$data = request()->all();
// 调用通用验证类,指定user_register场景
$errors = CommonValidator::validate('user_register', $data);
// 如果有错误,返回所有错误信息
if (!empty($errors)) {
return response()->json([
'code' => 400,
'msg' => '参数验证失败',
'errors' => $errors,
]);
}
// 验证通过,执行后续注册逻辑
// ...
return response()->json([
'code' => 200,
'msg' => '注册成功',
]);
}
// 用户修改资料接口
public function updateProfile(): JsonResponse
{
$data = request()->all();
// 指定user_update场景,只验证该场景下的字段
$errors = CommonValidator::validate('user_update', $data);
if (!empty($errors)) {
return response()->json([
'code' => 400,
'msg' => '参数验证失败',
'errors' => $errors,
]);
}
// 验证通过,执行修改资料逻辑
// ...
return response()->json([
'code' => 200,
'msg' => '资料修改成功',
]);
}
}扩展新的验证场景
如果后续需要新增验证场景,只需要在CommonValidator类的$sceneRules和$sceneMessages属性中添加对应的配置即可,不需要修改验证逻辑的核心代码,比如新增文章发布场景:
// 在$sceneRules中添加
'article_publish' => [
'title' => 'required|max:100',
'content' => 'required|min:10',
'category_id' => 'required|integer|min:1',
],
// 在$sceneMessages中添加
'article_publish' => [
'title.required' => '文章标题不能为空',
'title.max' => '文章标题不能超过100位',
'content.required' => '文章内容不能为空',
'content.min' => '文章内容不能少于10位',
'category_id.required' => '文章分类不能为空',
'category_id.integer' => '文章分类ID必须是整数',
],注意事项
- 验证规则中的
sometimes表示只有当字段存在时才验证,适合可选字段的验证场景 - 如果需要自定义验证规则,可以在通用类中添加自定义验证方法,然后在规则中引用即可
- 返回的错误信息数组包含所有字段的错误,前端可以根据需要展示全部或者筛选展示
- 如果验证场景较多,也可以把场景配置拆分成单独的配置文件,方便维护
通过这种方式封装的通用验证类,可以很好地满足Laravel中多个字段选择性验证并返回所有错误的需求,减少重复的验证代码,提升开发效率。