Hyperf数据验证:规则定义与自定义验证器开发

Hyperf数据验证:规则定义与自定义验证器开发

【免费下载链接】hyperf 🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease. 【免费下载链接】hyperf 项目地址: https://gitcode.com/gh_mirrors/hy/hyperf

你是否还在为API接口的数据验证焦头烂额?是否在重复编写大量if-else判断合法性?Hyperf框架提供了强大的数据验证组件,让你轻松实现复杂的验证逻辑。本文将从基础规则到高级自定义验证器,全方位解析Hyperf数据验证系统,读完你将掌握:

  • 50+内置验证规则的灵活应用
  • 表单请求(FormRequest)的优雅实践
  • 自定义验证器的完整开发流程
  • 复杂场景下的验证策略(数组验证、条件验证等)

验证组件核心架构

Hyperf验证组件衍生于Laravel的illuminate/validation,保留了其强大的规则系统同时适配了Swoole协程环境。核心类结构如下:

mermaid

验证流程包含三个关键环节:

  1. 规则定义:声明字段验证规则集合
  2. 数据验证:Validator实例执行验证逻辑
  3. 结果处理:通过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' => 'nullableinteger'
boolean必须为布尔值'is_active' => 'boolean'
integer必须为整数'score' => 'integermin:0max:100'
numeric必须为数值'price' => 'numericbetween:0.01,9999.99'
string必须为字符串'title' => 'stringmax:255'
array必须为数组'tags' => 'array'

字符串验证

规则描述示例
alpha仅字母'username' => 'alphamin:3max:20'
alpha_num字母和数字'password' => 'alpha_nummin:8'
email邮箱格式'email' => 'requiredemail'
regex正则匹配'phone' => 'regex:/^1[3-9]\d{9}$/'
urlURL格式'website' => 'url'
ipIP地址'server_ip' => 'ip'

数值验证

规则描述示例
between:min,max介于范围之间'quantity' => 'integerbetween:1,100'
min:value最小值'age' => 'integermin:18'
max:value最大值'score' => 'integermax: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' => 'imagemax: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' => 'requiredconfirmed'
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_ifrequired_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');
});

性能优化与最佳实践

验证性能优化

  1. 规则顺序优化:将快速验证的规则(如required、nullable)放在前面,复杂规则(如unique、exists)放在后面
  2. 数据库规则缓存:对高频验证的exists规则结果进行缓存
  3. 批量验证:避免循环内多次创建验证器实例
  4. 按需验证:使用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数据验证组件提供了从简单到复杂场景的完整解决方案,核心优势:

  1. 规则丰富:50+内置规则覆盖大部分业务需求
  2. 灵活扩展:多种自定义规则实现方式
  3. 性能优异:适配Swoole协程环境,无阻塞验证
  4. 场景化:通过scene机制支持多场景复用

进阶学习方向:

  • 深入理解验证器事件系统
  • 开发领域特定验证规则库
  • 实现验证规则可视化配置

掌握Hyperf数据验证不仅能提升代码质量,更能大幅减少重复劳动,让开发者专注于核心业务逻辑。建议结合实际项目封装适合团队的验证规范和复用组件。

本文示例代码已同步至Hyperf官方示例仓库,欢迎Star关注项目最新动态。如有疑问或建议,欢迎在评论区留言讨论。

延伸阅读

【免费下载链接】hyperf 🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease. 【免费下载链接】hyperf 项目地址: https://gitcode.com/gh_mirrors/hy/hyperf

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值