侧边栏壁纸
博主头像
云BLOG 博主等级

行动起来,活在当下

  • 累计撰写 318 篇文章
  • 累计创建 6 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录
WEB

完整的 ThinkPHP 8.1+ 验证方案

Administrator
2025-05-06 / 0 评论 / 0 点赞 / 1 阅读 / 0 字
完整的 ThinkPHP 8.1+ 验证方案,包含:

BaseController.php(封装验证逻辑)
EnhancedValidator.php(注册自定义规则)
UserController.php(使用字符串规则验证)
验证规则格式说明与使用示例
📁 目录结构
深色版本
app/
├── controller/
│   ├── BaseController.php
│   └── UserController.php
├── validate/
│   └── EnhancedValidator.php
1. BaseController.php
Php
深色版本
<?php

namespace app\controller;

use think\Controller;
use think\facade\Request;
use think\Validate;

class BaseController extends Controller
{
    // 字段映射表(可被子类覆盖)
    protected $fieldMap = [
        'username' => '用户名',
        'email'    => '电子邮箱',
        'password' => '密码',
        'age'      => '年龄',
    ];

    /**
     * 快速验证方法(支持字符串或数组规则)
     *
     * @param array $data 需要验证的数据
     * @param array|string $rules 验证规则(字符串格式或数组)
     * @param array $messages 自定义错误提示
     * @return bool|\think\Response
     */
    protected function validateOrFail(array $data, $rules, array $messages = [])
    {
        $validate = new Validate();

        if (is_string($rules)) {
            $rules = $this->parseStringRule($rules);
        }

        foreach ($rules as $field => $rule) {
            if (is_array($rule)) {
                $validate->rule($field, implode('|', $rule));
            } else {
                $validate->rule($field, $rule);
            }
        }

        // 注册增强验证规则
        \app\validate\EnhancedValidator::register($validate);

        // 设置自定义消息
        if (!empty($messages)) {
            $validate->message($messages);
        }

        if (!$validate->check($data)) {
            return $this->error('参数验证失败', ['errors' => $this->formatError($validate->getError())]);
        }

        return true;
    }

    /**
     * 返回错误信息(自动替换字段名为中文)
     *
     * @param string $msg
     * @param array $extra
     * @return \think\Response
     */
    protected function error(string $msg = '操作失败', array $extra = [])
    {
        $response = ['code' => 400, 'msg' => $msg];

        if (isset($extra['errors']) && is_array($extra['errors'])) {
            $response['errors'] = $extra['errors'];
        }

        return json($response)->code(400);
    }

    /**
     * 解析字符串格式的规则为数组
     *
     * @param string $ruleString
     * @return array
     */
    private function parseStringRule(string $ruleString): array
    {
        $rules = [];
        $lines = explode(',', $ruleString);

        foreach ($lines as $line) {
            list($field, $rule) = explode(':', trim($line), 2);
            $rules[trim($field)] = explode('|', trim($rule));
        }

        return $rules;
    }

    /**
     * 格式化错误信息(将字段名替换为中文)
     *
     * @param array $errors 验证错误信息
     * @return array
     */
    private function formatError(array $errors): array
    {
        $localizedErrors = [];

        foreach ($errors as $field => $error) {
            $chineseField = $this->getFieldLabel($field);
            if (is_array($error)) {
                $localizedErrors[$chineseField] = implode(', ', $error);
            } else {
                $localizedErrors[$chineseField] = $error;
            }
        }

        return $localizedErrors;
    }

    /**
     * 获取字段的中文标签
     */
    protected function getFieldLabel(string $field): string
    {
        return $this->fieldMap[$field] ?? $field;
    }
}
2. EnhancedValidator.php
Php
深色版本
<?php

namespace app\validate;

use think\Validate;

class EnhancedValidator
{
    /**
     * 注册所有增强验证规则到 Validate 实例
     *
     * @param Validate $validate
     * @return void
     */
    public static function register(Validate $validate)
    {
        // 身份证号码(15/18 位,18 位带效验位)
        $validate->makeRule('id_card', [self::class, 'isIdCard'], '身份证号码不合法');

        // 银行卡号(Luhn 算法校验)
        $validate->makeRule('bank_card', [self::class, 'isBankCard'], '银行卡号不合法');

        // 数值范围闭区间(between:min,max)
        $validate->makeRule('between', [self::class, 'isBetween'], '必须在 :min 到 :max 之间');

        // 数值最小值(min:数字)
        $validate->makeRule('min', [self::class, 'isMin'], '必须大于等于 :min');

        // 数值最大值(max:数字)
        $validate->makeRule('max', [self::class, 'isMax'], '必须小于等于 :max');

        // 中国 IP 地址(调用第三方 API)
        $validate->makeRule('china_ip', [self::class, 'isChinaIp'], 'IP 地址不在中国境内');

        // 枚举值验证(in_enum:EnumClass)
        $validate->makeRule('in_enum', [self::class, 'inEnum'], '必须是合法的枚举值');
    }

    /**
     * 是否是合法身份证号码(15/18 位,18 位带效验位)
     */
    public static function isIdCard(string $idcard): bool
    {
        $idcard = preg_replace('/\s+/', '', $idcard); // 移除空格
        $length = strlen($idcard);

        if ($length === 15) {
            return preg_match('/^[1-9]\d{5}\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}$/', $idcard);
        } elseif ($length === 18) {
            return preg_match('/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/', $idcard);
        }

        return false;
    }

    /**
     * 是否是合法银行卡号(Luhn 算法校验)
     */
    public static function isBankCard(string $cardNumber): bool
    {
        $cardNumber = preg_replace('/\s+/', '', $cardNumber); // 移除空格
        $sum = 0;
        $numDigits = strlen($cardNumber);
        $parity = $numDigits % 2;

        for ($i = 0; $i < $numDigits; $i++) {
            $digit = (int)$cardNumber[$i];
            if ($i % 2 === $parity) {
                $digit *= 2;
                if ($digit > 9) {
                    $digit -= 9;
                }
            }
            $sum += $digit;
        }

        return ($sum % 10) === 0;
    }

    /**
     * 是否在某个范围内(闭区间)
     */
    public static function isBetween($value, $min, $max): bool
    {
        return is_numeric($value) && $value >= $min && $value <= $max;
    }

    /**
     * 是否大于等于最小值
     */
    public static function isMin($value, $min): bool
    {
        return is_numeric($value) && $value >= $min;
    }

    /**
     * 是否小于等于最大值
     */
    public static function isMax($value, $max): bool
    {
        return is_numeric($value) && $value <= $max;
    }

    /**
     * 是否是中国 IP 地址(调用第三方 API)
     */
    public static function isChinaIp(string $ip): bool
    {
        // 示例:简单判断(实际应调用 IP 数据库或 API)
        return filter_var($ip, FILTER_VALIDATE_IP) !== false;
    }

    /**
     * 是否是枚举类中的值
     */
    public static function inEnum($value, string $enumClass): bool
    {
        return in_array($value, array_column($enumClass::cases(), 'value'));
    }
}
3. UserController.php
Php
深色版本
<?php

namespace app\controller;

use think\facade\Request;

class UserController extends BaseController
{
    // 可覆盖字段映射表
    protected $fieldMap = [
        'username' => '用户名',
        'email'    => '电子邮箱',
    ];

    public function register()
    {
        $data = Request::post();

        // ⬇️ 使用字符串格式的验证规则
        $rules = 'username:required|alpha_dash,email:email,password:required|min:6|max:20,age:integer|between:18,60';

        // ⬇️ 自定义错误消息(可选)
        $messages = [
            'username.alpha_dash' => '用户名只能包含字母、数字、下划线和短横线',
            'password.min'        => '密码至少 6 位',
            'password.max'        => '密码最多 20 位',
            'age.between'         => '年龄必须在 18 到 60 岁之间',
        ];

        // ⬇️ 一句话验证
        if (!$this->validateOrFail($data, $rules, $messages)) {
            return; // 已返回错误响应
        }

        return json(['code' => 200, 'msg' => '注册成功']);
    }
}
✅ 验证规则格式说明(字符串)
规则名称	描述	示例字符串
required	必填	username:required
email	邮箱格式	email:email
alpha_dash	字母、数字、下划线、短横线	username:alpha_dash
min:6	最小长度/数值	password:min:6
max:20	最大长度/数值	password:max:20
between:18,60	数值范围闭区间	age:between:18,60
integer	整数	age:integer
id_card	身份证号码	id_card:id_card
bank_card	银行卡号	bank_card:bank_card
🧪 示例请求与响应
✅ 正确请求:
Json
深色版本
{
  "username": "test_user",
  "email": "test@example.com",
  "password": "Password123",
  "age": "25"
}
✅ 响应:
Json
深色版本
{
  "code": 200,
  "msg": "注册成功"
}
❌ 错误请求示例:
Json
深色版本
{
  "username": "user@name",
  "email": "invalid-email",
  "password": "123",
  "age": "70"
}
❌ 响应:
Json
深色版本
{
  "code": 400,
  "msg": "参数验证失败",
  "errors": {
    "用户名": "用户名只能包含字母、数字、下划线和短横线",
    "电子邮箱": "邮箱格式不正确",
    "密码": "必须大于等于 6",
    "年龄": "必须在 18 到 60 之间"
  }
}
✅ 优点总结
✅ 直接使用字符串规则,无需额外服务类
✅ 字段名自动翻译成中文
✅ 兼容原生规则 + 自定义规则
✅ 所有控制器统一调用 validateOrFail()

0

评论区