Hyperf数据验证:规则定义与自定义验证器开发
你是否还在为API接口的数据验证焦头烂额?是否在重复编写大量if-else判断合法性?Hyperf框架提供了强大的数据验证组件,让你轻松实现复杂的验证逻辑。本文将从基础规则到高级自定义验证器,全方位解析Hyperf数据验证系统,读完你将掌握:
- 50+内置验证规则的灵活应用
- 表单请求(FormRequest)的优雅实践
- 自定义验证器的完整开发流程
- 复杂场景下的验证策略(数组验证、条件验证等)
验证组件核心架构
Hyperf验证组件衍生于Laravel的illuminate/validation,保留了其强大的规则系统同时适配了Swoole协程环境。核心类结构如下:
验证流程包含三个关键环节:
- 规则定义:声明字段验证规则集合
- 数据验证:Validator实例执行验证逻辑
- 结果处理:通过MessageBag获取错误信息或验证后数据
环境准备与基础配置
组件安装
composer require hyperf/validation
中间件配置
在config/autoload/middlewares.php中添加全局中间件:
return [
'http' => [
\Hyperf\Validation\Middleware\ValidationMiddleware::class,
// 其他中间件...
],
];
异常处理
配置config/autoload/exceptions.php添加验证异常处理器:
return [
'handler' => [
'http' => [
\Hyperf\Validation\ValidationExceptionHandler::class,
// 其他异常处理器...
],
],
];
语言文件发布
# 发布翻译组件配置
php bin/hyperf.php vendor:publish hyperf/translation
# 发布验证器语言文件
php bin/hyperf.php vendor:publish hyperf/validation
生成的语言文件位于storage/languages/zh_CN/validation.php,可自定义验证提示信息。
内置验证规则全解析
Hyperf提供50+内置验证规则,覆盖绝大多数业务场景。按功能分类如下:
基础类型验证
| 规则 | 描述 | 示例 | ||
|---|---|---|---|---|
| required | 字段必填 | 'name' => 'required' | ||
| nullable | 允许为null | 'age' => 'nullable | integer' | |
| boolean | 必须为布尔值 | 'is_active' => 'boolean' | ||
| integer | 必须为整数 | 'score' => 'integer | min:0 | max:100' |
| numeric | 必须为数值 | 'price' => 'numeric | between:0.01,9999.99' | |
| string | 必须为字符串 | 'title' => 'string | max:255' | |
| array | 必须为数组 | 'tags' => 'array' |
字符串验证
| 规则 | 描述 | 示例 | ||
|---|---|---|---|---|
| alpha | 仅字母 | 'username' => 'alpha | min:3 | max:20' |
| alpha_num | 字母和数字 | 'password' => 'alpha_num | min:8' | |
| 邮箱格式 | 'email' => 'required | email' | ||
| regex | 正则匹配 | 'phone' => 'regex:/^1[3-9]\d{9}$/' | ||
| url | URL格式 | 'website' => 'url' | ||
| ip | IP地址 | 'server_ip' => 'ip' |
数值验证
| 规则 | 描述 | 示例 | |
|---|---|---|---|
| between:min,max | 介于范围之间 | 'quantity' => 'integer | between:1,100' |
| min:value | 最小值 | 'age' => 'integer | min:18' |
| max:value | 最大值 | 'score' => 'integer | max:100' |
| in:foo,bar | 在列表中 | 'status' => 'in:active,inactive,pending' | |
| not_in:foo,bar | 不在列表中 | 'role' => 'not_in:admin,superuser' | |
| distinct | 数组无重复值 | 'ids.*' => 'distinct' |
文件验证
| 规则 | 描述 | 示例 | |
|---|---|---|---|
| file | 必须是上传文件 | 'avatar' => 'file' | |
| image | 必须是图片类型 | 'photo' => 'image | max:2048' |
| mimes:type | 允许的文件类型 | 'document' => 'mimes:pdf,doc,docx' | |
| dimensions | 图片尺寸约束 | 'avatar' => 'dimensions:min_width=100,min_height=100' |
高级验证
| 规则 | 描述 | 示例 | |
|---|---|---|---|
| required_if:field,value | 依赖字段等于指定值时必填 | 'parent_id' => 'required_if:type,sub' | |
| required_with:field1,field2 | 当指定字段存在时必填 | 'address' => 'required_with:shipping_method' | |
| confirmed | 需要确认字段 | 'password' => 'required | confirmed' |
| exists:table,column | 数据库存在性验证 | 'user_id' => 'exists:users,id' | |
| unique:table,column | 数据库唯一性验证 | 'email' => 'unique:users,email' |
规则组合示例
// 用户注册验证规则
[
'username' => 'required|alpha_dash|between:3,20|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|alpha_num|min:8|confirmed',
'age' => 'nullable|integer|between:18,120',
'avatar' => 'nullable|image|dimensions:ratio=1/1|max:2048',
'interests' => 'array|min:1',
'interests.*' => 'string|in:reading,sports,music',
'phone' => 'required_if:contact_method,phone|regex:/^1[3-9]\d{9}$/'
]
表单请求验证(FormRequest)
FormRequest是一种优雅的验证方式,将验证逻辑与控制器解耦。
基本使用
生成FormRequest类:
php bin/hyperf.php gen:request UserRequest
基础实现:
<?php
declare(strict_types=1);
namespace App\Request;
use Hyperf\Validation\Request\FormRequest;
class UserRequest extends FormRequest
{
// 授权检查
public function authorize(): bool
{
// 通常返回true,复杂权限可在此实现
return true;
}
// 验证规则
public function rules(): array
{
return [
'username' => 'required|alpha_dash|between:3,20',
'email' => 'required|email',
'password' => 'required|min:8',
];
}
// 自定义错误消息
public function messages(): array
{
return [
'username.required' => '用户名不能为空',
'username.alpha_dash' => '用户名只能包含字母、数字和下划线',
'email.email' => '请输入有效的邮箱地址',
];
}
// 自定义属性名
public function attributes(): array
{
return [
'username' => '用户名',
'email' => '邮箱地址',
'password' => '密码',
];
}
}
在控制器中使用:
<?php
namespace App\Controller;
use App\Request\UserRequest;
class UserController extends Controller
{
public function store(UserRequest $request)
{
// 验证通过的请求数据
$validated = $request->validated();
// 业务逻辑处理...
return $this->success($validated);
}
}
场景验证
支持多场景验证,适用于同一表单不同操作(如创建/更新):
class UserRequest extends FormRequest
{
// 定义场景
protected array $scenes = [
'create' => ['username', 'email', 'password'],
'update' => ['username', 'email']
];
public function rules(): array
{
return [
'username' => 'required|alpha_dash|between:3,20|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|min:8',
];
}
}
控制器中切换场景:
public function update(UserRequest $request, int $id)
{
// 更新场景排除password字段,unique规则排除当前记录
$request->scene('update')->merge(['id' => $id]);
$validated = $request->validated();
// 更新逻辑...
}
注解场景验证
使用注解更直观地指定场景:
use Hyperf\Validation\Annotation\Scene;
class UserController extends Controller
{
#[Scene(scene: 'create')]
public function create(UserRequest $request)
{
// 创建用户逻辑
}
#[Scene(scene: 'update')]
public function update(UserRequest $request, int $id)
{
// 更新用户逻辑
}
// 默认场景名为方法名
#[Scene]
public function delete(UserRequest $request)
{
// 删除用户逻辑
}
}
手动创建验证器
除FormRequest外,可通过ValidatorFactory手动创建验证器:
<?php
namespace App\Service;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Validation\Contract\ValidatorFactoryInterface;
class UserService
{
#[Inject]
protected ValidatorFactoryInterface $validatorFactory;
public function create(array $data)
{
// 创建验证器实例
$validator = $this->validatorFactory->make(
$data,
[
'username' => 'required|alpha_dash|between:3,20',
'email' => 'required|email',
],
[
'username.required' => '用户名不能为空',
],
[
'username' => '用户名',
]
);
// 验证失败处理
if ($validator->fails()) {
// 获取错误信息
$errors = $validator->errors()->all();
// 抛出异常或其他处理...
throw new \RuntimeException(implode(',', $errors));
}
// 获取验证通过的数据
$validated = $validator->validated();
// 业务逻辑处理...
return $validated;
}
}
验证后钩子
添加验证通过后的额外检查:
$validator->after(function ($validator) use ($data) {
if ($this->isUsernameExists($data['username'])) {
$validator->errors()->add('username', '用户名已被使用');
}
if ($data['password'] !== $data['password_confirmation']) {
$validator->errors()->add('password', '两次密码输入不一致');
}
});
复杂数据验证
数组验证
对数组类型字段进行验证:
// 验证规则
[
'products' => 'required|array|min:1',
'products.*.id' => 'required|integer|exists:products,id',
'products.*.quantity' => 'required|integer|min:1|max:10',
'products.*.price' => 'required|numeric|min:0.01'
]
// 对应请求数据格式
[
'products' => [
['id' => 1, 'quantity' => 2, 'price' => 99.99],
['id' => 2, 'quantity' => 1, 'price' => 199.99]
]
]
在语言文件中自定义数组字段错误信息:
'custom' => [
'products.*.id' => [
'required' => '产品ID不能为空',
'exists' => '产品不存在'
],
'products.*.quantity' => [
'min' => '产品数量不能小于1',
'max' => '产品数量不能大于10'
]
]
条件验证
使用required_if、required_with等规则实现条件验证:
// 用户注册规则示例
[
'type' => 'required|in:individual,company',
'name' => 'required|string|max:50',
// 个人用户必填字段
'id_card' => 'required_if:type,individual|regex:/^\d{17}[\dXx]$/',
// 企业用户必填字段
'company_name' => 'required_if:type,company|string|max:100',
'business_license' => 'required_if:type,company|file|mimes:pdf,jpg,png',
// 联系方式至少填一种
'phone' => 'required_without_all:email,wechat',
'email' => 'required_without_all:phone,wechat|email',
'wechat' => 'required_without_all:phone,email|string'
]
更复杂的条件可使用Rule类:
use Hyperf\Validation\Rule;
[
'role_id' => Rule::requiredIf(function () use ($request) {
return $request->input('is_admin') === true;
}),
'permissions' => Rule::requiredIf(fn () => $this->needsPermissions()),
]
数据库验证规则高级用法
unique规则排除当前记录(更新场景):
use Hyperf\Validation\Rule;
[
'email' => [
'required',
'email',
Rule::unique('users')->ignore($user->id),
],
]
// 指定数据库连接和字段
Rule::unique('mysql_connection.users', 'email_address')->ignore($user->id, 'user_id')
// 附加查询条件
Rule::unique('users')->where(function ($query) {
return $query->where('status', 1);
})
exists规则高级查询:
// 验证部门ID存在且状态为启用
'department_id' => Rule::exists('departments')->where('status', 1)
自定义验证规则开发
扩展验证规则
通过事件监听注册自定义规则,创建监听器:
<?php
declare(strict_types=1);
namespace App\Listener;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Validation\Event\ValidatorFactoryResolved;
use Hyperf\Validation\Validator;
#[Listener]
class ValidatorFactoryResolvedListener implements ListenerInterface
{
public function listen(): array
{
return [
ValidatorFactoryResolved::class,
];
}
public function process(object $event): void
{
// 获取验证器工厂实例
$validatorFactory = $event->validatorFactory;
// 注册身份证验证规则
$validatorFactory->extend('id_card', function (string $attribute, mixed $value, array $parameters, Validator $validator): bool {
// 简化版身份证验证逻辑
if (!is_string($value) || strlen($value) !== 18) {
return false;
}
// 正则验证
if (!preg_match('/^\d{17}[\dXx]$/', $value)) {
return false;
}
// 此处可添加更复杂的校验算法...
return true;
}, ':attribute格式不正确');
// 注册手机号验证规则
$validatorFactory->extend('mobile', function (string $attribute, mixed $value): bool {
return is_string($value) && preg_match('/^1[3-9]\d{9}$/', $value);
});
// 带参数的自定义规则 - 示例:检查值是否在指定范围内
$validatorFactory->extend('range', function (string $attribute, mixed $value, array $parameters): bool {
if (count($parameters) < 2) {
return false; // 参数不足
}
[$min, $max] = $parameters;
return $value >= $min && $value <= $max;
}, ':attribute必须在:min到:max之间');
}
}
自定义错误消息替换器
为自定义规则添加参数化错误消息:
// 注册替换器
$validatorFactory->replacer('range', function (string $message, string $attribute, string $rule, array $parameters): string {
return str_replace([':min', ':max'], $parameters, $message);
});
// 在语言文件中定义
'range' => ':attribute必须在:min到:max之间',
使用自定义规则
在验证规则中直接使用:
// FormRequest中使用
public function rules(): array
{
return [
'id_card' => 'required|id_card',
'mobile' => 'required|mobile',
'score' => 'required|integer|range:0,100',
];
}
规则类实现方式
对于复杂规则,可创建独立规则类:
<?php
declare(strict_types=1);
namespace App\Validation\Rules;
use Hyperf\Validation\Contract\Rule;
class IdCardRule implements Rule
{
public function passes(string $attribute, mixed $value): bool
{
// 实现身份证验证逻辑
if (!is_string($value) || strlen($value) !== 18) {
return false;
}
// 正则验证
if (!preg_match('/^\d{17}[\dXx]$/', $value)) {
return false;
}
// 校验码算法验证...
return $this->checkVerifyCode($value);
}
public function message(): string
{
return ':attribute格式不正确';
}
private function checkVerifyCode(string $idCard): bool
{
// 实现校验码验证逻辑
// ...
return true;
}
}
使用规则类:
use App\Validation\Rules\IdCardRule;
public function rules(): array
{
return [
'id_card' => ['required', new IdCardRule()],
];
}
高级验证技巧
验证后数据处理
使用after钩子实现验证后的数据处理:
$validator->after(function ($validator) {
$data = $validator->validated();
// 处理密码加密
if (isset($data['password'])) {
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
$validator->setData($data);
}
// 数据转换
if (isset($data['birth_date'])) {
$data['birth_date'] = Carbon::parse($data['birth_date'])->format('Y-m-d');
$validator->setData($data);
}
});
复杂嵌套数组验证
对多层嵌套数组进行验证:
// 订单提交数据验证规则
[
'order_no' => 'required|string|max:50',
'user_id' => 'required|integer|exists:users,id',
'items' => 'required|array|min:1',
'items.*.product_id' => 'required|integer|exists:products,id',
'items.*.quantity' => 'required|integer|min:1',
'items.*.price' => 'required|numeric|min:0.01',
'address' => 'required|array',
'address.province' => 'required|string',
'address.city' => 'required|string',
'address.detail' => 'required|string|max:200',
'payment' => 'required|array',
'payment.method' => 'required|in:alipay,wechat,bank',
'payment.account' => 'required|string'
]
跨字段依赖验证
实现字段间复杂依赖关系验证:
// 示例:开始时间必须早于结束时间
$validator->after(function ($validator) {
$startTime = $validator->getData()['start_time'] ?? null;
$endTime = $validator->getData()['end_time'] ?? null;
if ($startTime && $endTime && strtotime($startTime) >= strtotime($endTime)) {
$validator->errors()->add('end_time', '结束时间必须晚于开始时间');
}
});
批量验证与部分验证
// 批量验证多个数据集
$validator = $this->validatorFactory->make(
$data,
$rules
);
if ($validator->fails()) {
// 获取所有错误
$errors = $validator->errors()->toArray();
// 处理错误...
}
// 部分验证 - 只验证存在的字段
$validator->sometimes('optional_field', 'required|string', function ($input) {
return $input->has('some_condition');
});
性能优化与最佳实践
验证性能优化
- 规则顺序优化:将快速验证的规则(如required、nullable)放在前面,复杂规则(如unique、exists)放在后面
- 数据库规则缓存:对高频验证的exists规则结果进行缓存
- 批量验证:避免循环内多次创建验证器实例
- 按需验证:使用sometimes方法只在必要时应用规则
最佳实践
规则复用
创建规则常量类集中管理常用规则:
<?php
namespace App\Constants;
class ValidationRules
{
// 手机号规则
public const MOBILE = 'required|regex:/^1[3-9]\d{9}$/';
// 邮箱规则
public const EMAIL = 'required|email|max:255';
// 密码规则
public const PASSWORD = 'required|alpha_num|min:8|max:20';
// 分页参数规则
public const PAGE = [
'page' => 'integer|min:1',
'per_page' => 'integer|min:1|max:100'
];
}
// 使用
public function rules(): array
{
return [
'mobile' => ValidationRules::MOBILE,
'email' => ValidationRules::EMAIL,
'password' => ValidationRules::PASSWORD,
];
}
API友好的错误格式
自定义异常处理器返回标准化错误格式:
// 自定义ValidationExceptionHandler
public function handle(Throwable $throwable, ResponseInterface $response)
{
if ($throwable instanceof ValidationException) {
$errors = $throwable->validator->errors()->toArray();
// 标准化错误响应格式
$data = [
'code' => 422,
'message' => '验证失败',
'errors' => $errors,
'request_id' => context()->get('request_id')
];
return $response->json($data, 422);
}
return parent::handle($throwable, $response);
}
测试验证规则
为关键验证规则编写单元测试:
<?php
declare(strict_types=1);
namespace Tests\Unit\Validation;
use App\Request\UserRequest;
use Hyperf\Validation\ValidatorFactory;
use Tests\TestCase;
class UserValidationTest extends TestCase
{
public function test_user_request_validation()
{
$request = make(UserRequest::class);
// 测试验证失败场景
$data = [
'username' => 'ab', // 太短
'email' => 'invalid-email',
'password' => '123' // 太短
];
$validator = $this->container->get(ValidatorFactory::class)->make(
$data,
$request->rules(),
$request->messages(),
$request->attributes()
);
$this->assertTrue($validator->fails());
$this->assertCount(3, $validator->errors()->all());
// 测试验证通过场景
$data = [
'username' => 'valid_username',
'email' => 'test@example.com',
'password' => 'valid_password_123'
];
$validator = $this->container->get(ValidatorFactory::class)->make(
$data,
$request->rules()
);
$this->assertFalse($validator->fails());
}
}
常见问题与解决方案
1. 协程环境下的验证器复用问题
问题:在长生命周期的协程中重复使用FormRequest可能导致数据污染
解决方案:每次请求都会创建新的FormRequest实例,无需额外处理
2. 复杂业务逻辑验证
方案:结合after钩子和服务层验证,将复杂逻辑封装到服务类
public function rules(): array
{
return [
// 基础规则...
];
}
public function afterValidation()
{
$this->container->get(UserService::class)->validateBusinessRules($this->validated());
}
3. 自定义规则参数解析
问题:自定义规则需要多个复杂参数
解决方案:使用JSON格式传递参数并在规则中解析
// 规则定义
'custom_rule' => 'required|custom_rule:{"param1":1,"param2":"value"}'
// 规则实现
public function passes(string $attribute, mixed $value): bool
{
$params = json_decode($this->parameters[0], true);
// 使用$params...
}
4. 验证器事件监听
解决方案:通过事件系统监听验证事件,实现全局拦截
// 定义事件
class ValidationEvent
{
public $validator;
public $data;
public function __construct($validator, $data)
{
$this->validator = $validator;
$this->data = $data;
}
}
// 在FormRequest中触发
public function validated()
{
$data = parent::validated();
$this->container->get(EventDispatcherInterface::class)->dispatch(new ValidationEvent($this, $data));
return $data;
}
// 监听事件进行后续处理
class ValidationListener implements ListenerInterface
{
public function listen(): array
{
return [ValidationEvent::class];
}
public function process(object $event): void
{
// 记录验证日志等操作
}
}
总结与进阶
Hyperf数据验证组件提供了从简单到复杂场景的完整解决方案,核心优势:
- 规则丰富:50+内置规则覆盖大部分业务需求
- 灵活扩展:多种自定义规则实现方式
- 性能优异:适配Swoole协程环境,无阻塞验证
- 场景化:通过scene机制支持多场景复用
进阶学习方向:
- 深入理解验证器事件系统
- 开发领域特定验证规则库
- 实现验证规则可视化配置
掌握Hyperf数据验证不仅能提升代码质量,更能大幅减少重复劳动,让开发者专注于核心业务逻辑。建议结合实际项目封装适合团队的验证规范和复用组件。
本文示例代码已同步至Hyperf官方示例仓库,欢迎Star关注项目最新动态。如有疑问或建议,欢迎在评论区留言讨论。
延伸阅读:
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



