thinkphp 6 模型 validate() 默认不逐项校验数组字段,需用 each 规则或 tags.* 语法实现元素级验证,并配合 batch(true) 获取完整错误信息。

ThinkPHP 模型 validate() 方法默认不校验数组字段
直接调用 validate() 或设置 validate(true) 时,如果表单提交的是类似 tags[] 这样的数组字段(如多选标签、动态表单项),ThinkPHP 6 的模型验证器默认只检查字段是否存在,不会自动遍历数组每一项执行规则。这不是 bug,是设计如此——验证器把 tags 当成一个整体,而非多个独立值。
常见错误现象:['required', 'array'] 能拦住空值或非数组,但写 ['max:10'] 或自定义函数,它只对整个数组长度校验,不会对每个字符串元素做 max:10 限制。
- 若需逐项校验,必须显式展开数组,例如用
tags.0、tags.1等方式声明规则(适合固定长度) - 更通用的做法:在验证器中使用
each规则,配合闭包或内置规则 - 注意 ThinkPHP 6.0+ 才原生支持
each,5.x 需手动循环或改用场景验证
用 each 规则实现 tags[] 类字段的逐项校验
each 是 ThinkPHP 6 验证器提供的专用语法,用于对数组字段的每个元素单独应用规则。它不是函数,而是规则键名,后面跟一个规则数组或闭包。
使用场景:提交 tags[]=php&tags[]=thinkphp&tags[]=(空字符串)时,既要过滤空项,又要限制每项长度 ≤ 20 且不能含特殊字符。
protected $rule = [
'tags' => 'require|array|each:alphaNumDash|max:5',
'tags.*' => 'max:20|regex:/^[a-zA-Z0-9\u4e00-\u9fa5]+$/',
];
说明:tags 行确保字段存在且为数组、非空、最多 5 项;tags.* 行对每个元素单独校验长度和格式。两者可共存,ThinkPHP 会合并处理。
-
each:后面只能接字符串规则(如alphaNumDash),不能接闭包;闭包必须写在tags.*规则里 - 若用闭包校验单个元素,写法是
'tags.*' => function($value, $rule, $data) { return strlen($value) > 0; } - 注意
each不会自动跳过空值,需额外加filter:trim或在闭包里判断
批量校验失败时如何拿到具体哪一项出错
默认 $model->validate()->save($data) 失败后,$model->getError() 只返回第一条错误信息,比如“tags.0 格式错误”,无法知道全部违规项。这对前端反馈不友好。
正确做法是先调用验证器的 batch(true) 开启批量错误收集,再手动验证:
$validate = new \app\common\validate\ArticleValidate();
$validate->batch(true);
if (!$validate->check($data)) {
dump($validate->getError()); // 返回完整错误数组,如 ['tags.0' => '长度不能超过20', 'tags.2' => '不能包含空格']
}
-
batch(true)必须在check()前调用,模型上的validate(true)不触发批量模式 - 错误键名为
字段名.索引,前端可据此定位并高亮对应输入框 - 若数组含关联键(如
items[title]、items[content]),需用items.*+ 闭包解析结构,each不适用
自定义验证方法里怎么安全处理 $_POST 中的数组字段
绕过验证器、在模型 save() 前手动校验时,容易忽略 PHP 对 $_POST['tags'] 的自动转换逻辑:当只有一个值时,它不是数组而是字符串,直接 foreach 会报 Warning。
安全写法永远先规范数据结构:
$data = input('post.');
$data['tags'] = is_array($data['tags']) ? $data['tags'] : ($data['tags'] ? [$data['tags']] : []);
- 别依赖
is_array($_POST['x'])判断,因为浏览器不提交该字段时,$_POST['x']是NULL,不是数组也不是字符串 - 用
input('tags/a')(/a表示强制转数组)比手动判断更可靠 - 对每个元素 trim 后再校验,否则
" php "过了alphaNumDash却卡在业务逻辑里
复杂点在于嵌套数组(如 options[][name] 和 options[][value]),这时候 each 失效,得用验证器的 scene + 闭包逐层 unpack,或者改用 FormRequest 封装。这种结构一旦出现,基本意味着该字段该抽成独立模型了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











