thinkphp-验证场景和路由验证、验证内置规则、单个验证和注解验证
验证场景
验证场景设置,即特定的场景下是否进行验证,独立验证不存在场景验证;
举一个简单的例子,比如一个类中有新增方法和修改方法,新增数据需要验证邮箱,而修改更新时不验证邮箱,而他们的验证信息写在验证类里,是通用的,所以这时候需要设置一个场景,给谁验证,给谁不验证。
可以在验证类app/validate/User.php 中,设置一个$scene 属性,用来限定场景验证;
<?php
declare (strict_types = 1);
namespace app\validate;
use think\Validate;
class User extends Validate
{
/**
* 定义验证规则
* 格式:'字段名' => ['规则1','规则2'...]
*
* @var array
*/
protected $rule = [
'name|用户名' => 'require|max:20',
'price' => 'number|between:1,100',
'email' => 'email'
];
/**
* 定义错误信息
* 格式:'字段名.规则名' => '错误信息'
*
* @var array
*/
protected $message = [
'name.require' => '姓名不得为空',
'name.max' => '姓名不得大于20 位',
'price.number' => '价格必须是数字',
'price.between' => '价格必须1-100 之间',
'email' => '邮箱的格式错误'
];
protected $scene = [
// 新增时候验证那些规则
'insert' => ['name', 'price', 'email'],
// 修改时验证那些规则 键名称自定义即可 只要调用时候配对就行
'edit' => ['name', 'price']
];
}
在控制器端,验证时,根据不同的验证手段,绑定相关场景进行验证即可;
$validate->scene('edit')->check($data)
namespace app\controller;
// 引入额外的验证类-index
use app\validate\User;
use think\exception\ValidateException;
class Verify
{
public function index()
{
try {
// 使用助手函数,直接放一个验证类进去
validate(User::class)
->batch(true)
->scene('edit') // 写入场景规则,不验证邮箱
->check([
'name' => '',
'price' => 100,
'email' => 'xiaoxin@163.com'
]);
} catch(ValidateException $e) {
dump($e->getError());
}
}
}
在验证类端,可以设置一个公共方法对场景的细节进行定义;在app/validate/User.php
protected $scene = [
// 新增时候验证那些规则
'insert' => ['name', 'price', 'email'],
// 修改时验证那些规则 键名称自定义即可 只要调用时候配对就行
// 'edit' => ['name', 'price']
];
// 通过sceneEdit方法,替代上面的'edit' 注意命名规则sceneE(大写)
protected function sceneEdit()
{
// only 方法指定到底执行那些规则
$edit = $this->only(['name', 'price']);
return $edit;
}
比如下面这个验证
// 使用助手函数,直接放一个验证类进去
validate(User::class)
->batch(true)
->scene('edit') // 写入场景规则,不验证邮箱
->check([
'name' => '孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空',
'price' => 100,
'email' => 'xiaoxin163.com'
]);
protected $scene = [
// 新增时候验证那些规则
'insert' => ['name', 'price', 'email'],
// 修改时验证那些规则 键名称自定义即可 只要调用时候配对就行
// 'edit' => ['name', 'price']
];
// 通过sceneEdit方法,替代上面的'edit' 注意命名规则sceneE(大写)
protected function sceneEdit()
{
// only 方法指定到底执行那些规则
$edit = $this->only(['name', 'price'])
->remove('name', 'max') // remove('验证规则名', '要移除的规则 这里是移除max')
;
return $edit;
}
移除之后,就不会再验证name规则里面的max规则了。
validate(User::class)
->batch(true)
->scene('edit') // 写入场景规则,不验证邮箱
->check([
'name' => '孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空孙悟空',
'price' => '',
'email' => 'xiaoxin163.com'
]);
为其添加一个价格不能为空的验证规则
protected function sceneEdit()
{
// only 方法指定到底执行那些规则
$edit = $this->only(['name', 'price'])
->remove('name', 'max') // remove('验证规则名', '要移除的规则 这里是移除max')
->append('price', 'require'); //增加一个不能为空的限制
;
return $edit;
}
注意:请不要对一个字段进行两个或以上的移出和添加,会被覆盖;
remove('name', 'xxx|yyy|zzz')或remove('name', ['xxx', 'yyy', 'zzz']);
而不是,后面的会覆盖掉前面的
remove('name', 'xxx')->remove('name', 'yyy')->remove('name', 'zzz'); 错误!
路由验证
首先修改验证规则,添加id验证
<?php
declare (strict_types = 1);
namespace app\validate;
use think\Validate;
class User extends Validate
{
/**
* 定义验证规则
* 格式:'字段名' => ['规则1','规则2'...]
*
* @var array
*/
protected $rule = [
// 这里的名称不一定要与数据库一模一样 可以自己写自己的
'name|用户名' => 'require|max:20|checkName:wubin.work', //对应表中username字段,不得为空,不得大于20 位
'price' => 'number|between:1,100', //必须是数值,1-100 之间
'email' => 'email', //邮箱格式要正确
'id' => 'number|between:1,10'
];
/**
* 定义错误信息
* 格式:'字段名.规则名' => '错误信息'
*
* @var array
*/
protected $message = [
'name.require' => '姓名不得为空',
'name.max' => '姓名不得大于20 位',
'price.number' => '价格必须是数字',
'price.between' => '价格必须1-100 之间',
'email' => '邮箱的格式错误',
'id.number' => 'id必须是数字',
'id.between' => 'id必须在1-10之间'
];
protected $scene = [
// 新增时候验证那些规则
'insert' => ['name', 'price', 'email'],
// 修改时验证那些规则 键名称自定义即可 只要调用时候配对就行
// 'edit' => ['name', 'price']
// 只针对路由的验证,只验证id
'route' => ['id']
];
// 通过sceneEdit方法,替代上面的'edit' 注意命名规则sceneE(大写)
protected function sceneEdit()
{
// only 方法指定到底执行那些规则
$edit = $this->only(['name', 'price'])
->remove('name', 'max') // remove('验证规则名', '要移除的规则 这里是移除max')
->append('price', 'require'); //增加一个不能为空的限制
;
return $edit;
}
// 自定义规则 支持5个参数,$value, $rule最重要
protected function checkName($value, $rule, $data, $field, $title)
{
return $value != $rule ? true : '非法名称,不得注册';
}
}
在route/app.php中 先写一个路由,并添加验证规则,场景参数传入定义好的场景:route
Route::rule('vr/:id', 'Verify/route')->validate(\app\validate\User::class, 'route');
访问:http://localhost/xuexi/tp6/public/vr/4,如果验证失败,会抛出一个错误。
如果不使用验证器类,也可以使用独立的验证方式,也可以使用对象化;
Route::rule('vr/:id', 'Verify/route')
->validate([
'id' => 'number|between:1,10',
'email' => \think\validate\ValidateRule::isEmail(null, '邮箱格式不正确')
]);
当访问:http://localhost/xuexi/tp6/public/vr/4?email=11,会抛出邮箱格式不正确的错误。
注意这里的email是额外的参数,就是?之后的参数,而id则是/id?email=xxx
Route::rule('vr/:id', 'Verify/route')
->validate(
[
'id' => 'number|between:1,10',
'email' => \think\validate\ValidateRule::isEmail(null, '邮箱格式不正确')
],
null, // 第二个参数是场景,默认null,没有的话传入Null占位即可
// 第三个参数是错误提示
[
"id.number" => '编号必须是数字'
],
// 是否都验证,即显示所有不符合验证规则的结果 默认false
true
);
当最后一个参数传入true时候,http://localhost/xuexi/tp6/public/vr/xx?email=11,抛出的错误会显示:
#0 [0]ValidateException in Validate.php line 534
编号必须是数字
邮箱格式不正确
验证内置规则
https://www.kancloud.cn/manual/thinkphp6_0/1037629
如果打不开,就https://www.thinkphp.cn/doc 中找tp6->验证->内置规则
内置规则
内置的规则内容较多,并且严格区分大小写,这里按照类别一一列出;
静态方法支持两种形式,比如::number()或者isNumber()均可;
而require 是PHP 保留字,那么就必须用isRequire()或must();
格式验证类:
'field' => 'require', //不得为空::isRequire 或::must
'field' => 'number', //是否是纯数字,非负数非小数点
'field' => 'integer', //是否是整数
'field' => 'float', //是否是浮点数
'field' => 'boolean', //是否是布尔值,或者bool
'field' => 'email', //是否是email
'field' => 'array', //是否是数组
'field' => 'accepted', //是否是“yes”“no”“1”这三个值
'field' => 'date', //是否是有效日期
'field' => 'alpha', //是否是纯字母
'field' => 'alphaNum', //是否是字母和数字
'field' => 'alphaDash', //是否是字母和数字以及_-(下划线和破折号)
'field' => 'chs', //是否是纯汉字
'field' => 'chsAlpha', //是否是汉字字母
'field' => 'chsAlphaNum', //是否是汉字字母数字
'field' => 'chsDash', //是否是汉字字母数字以及_-(下划线和破折号)
'field' => 'cntrl', //是否是控制字符(换行、缩进、空格)
'field' => 'graph', //是否是可打印字符(空格除外)
'field' => 'print', //是否是可打印字符(包含空格)
'field' => 'lower', //是否是小写字符
'field' => 'upper', //是否是大写字符
'field' => 'space', //是否是空白字符
'field' => 'xdigit', //是否是十六进制
'field' => 'activeUrl', //是否是有效域名或IP 地址
'field' => 'url', //是否是有效URL 地址
'field' => 'ip', //是否是有效IP(支持ipv4,ipv6)
'field' => 'dateFormat:Y-m-d', //是否是指定日期格式
'field' => 'mobile', //是否是有效手机
'field' => 'idCard', //是否是有效身份证
'field' => 'macAddr', //是否是有效MAC 地址
'field' => 'zip', //是否是有效邮编
应用示例如下
// 使用门面模式引入验证类-rule
use think\facade\Validate;
use think\validate\ValidateRule;
$validate = Validate::rule([
'name' => ValidateRule::isRequire()->max(20),
'price' => ValidateRule::isNumber()->between([1, 100], '价格在1-100之间'),
'email' => ValidateRule::isEmail(null, '邮箱格式不正确'),
// test 不能为空
'test' => 'require'
'test' => ValidateRule::isRequire()
'test' => ValidateRule::must()
/*
后续规则会添加在此处
*/
]);
// 使用message方法 对错误信息单独定义
$validate->message([
'name.require' => ['code'=>1001, 'msg'=>'姓名不得为空'],
'name.max' => '姓名不得大于20 位',
...
]);
// 传入测试数据 对规则进行验证
$result = $validate->batch(true)->check([
'name' => '蜡笔小新',
'price' => 100,
'email' => 'xiaoxin@163.com',
'test' => ''
...
]);
// 如果没有结果 那么输出错误信息
if(!$result) {
dump($validate->getError());
}
验证必须是数字
// 必须是数字
'test' => ValidateRule::number()
'test' => ValidateRule::isNumber() 建议
必须是纯汉字
// 必须是纯汉字
'test' => 'chs'
'test' => ValidateRule::isChs()
长度和区间验证类:
'field' => 'in:1,2,3', //是否是有某个值
'field' => 'notIn:1,2,3', //是否是没有某个值
'field' => 'between:1,100', //是否是在区间中
'field' => 'notBetween:1,100', //是否是不在区间中
'field' => 'length:2,20', //是否字符长度在范围中
'field' => 'length:4', //是否字符长度匹配
'field' => 'max:20', //是否字符最大长度
'field' => 'min:5', //是否字符最小长度
//length、max、min 也可以判断数组长度和File 文件大小
'field' => 'after:2020-1-1', //是否在指定日期之后
'field' => 'before:2020-1-1', //是否在指定日期之前
//是否在当前操作是否在某个有效期内
'field' => 'expire:2019-1-1,2020-1-1',
//验证当前请求的IP 是否在某个范围之间,
'field' => 'allowIp:221.221.78.1, 192.168.0.1',
//验证当前请求的IP 是否被禁用
'field' => 'denyIp:221.221.78.1, 127.0.0.1',
区间长度
// 要求长度是4位
'test' => 'length:4'
'test' => ValidateRule::length(4)
// 长度区间2-4
'test' => 'length:2,4'
'test' => ValidateRule::length([2,4])
'test' => ValidateRule::length('2,4')
字段比较类
'field' => 'confirm:password', //是否和另一个字段匹配
'field' => 'differnet:password',//是否和另一个字段不匹配
'field' => 'eq:100', //是否等于某个值,=、same 均可
'field' => 'gt:100', //是否大于某个值,支持>
'field' => 'egt:100', //是否大于等于某个值,支持>=
'field' => 'lt:100', //是否小于某个值,支持<
'field' => 'elt:100', //是否小于等于某个值,支持<=
//比较方式也支持字段比较,比如:'field'=>'lt:price'
字符串比较类
'test' => 'eq:100' // 不比较类型
'test' => ValidateRule::eq(100)
'test' => ValidateRule::same(100)
不同字段进行比较(对密码与确认密码时候可以用)
'test' => 'confirm:price' // test字段与price字段进行比较
错误会返回
array:1 [
"test" => "test和确认字段不一致"
]
其它验证类:
'field' => '\d{6}', //正则表达式验证
'field' => 'regex:\d{6}', //正则表达式验证
'field' => 'file', //判断是否是上传文件
'field' => 'image:150,150,gif', //判断图片(参数可选)
'field' => 'fileExt:jpg,txt', //判断文件允许的后缀
'field' => 'fileMime:text/html',//判断文件允许的文件类型
'field' => 'fileSize:2048', //判断文件允许的字节大小
'field' => 'unique:user', //验证field 字段的值是否在user 表
'field' => 'requireWith:account',//当account 有值时,requireWidth 必须
'email' => 'requireWithout:name',
'name' => 'requireWithout:email', //name 和email 必须有一个有值
比如图片上传后的后缀之类的判断,以及对表中字段的判断。
比如我要判断我输入的名字,是否在user表中存在。都不需要写SQL语句
# 和表的字段比较
'username' => 'unique:user' // '表里面的字段名username' => unique(唯一性):哪张表
$result = $validate->batch(true)->check([
'username' => '蜡笔小新' // 表中存在username列中含有’蜡笔小新‘的值,所以会报错
# 'username' => 'wubin' // 因为表中不存在username列中含有wubin的值,所以不报错
]);
// 如果没有结果 那么输出错误信息
if(!$result) {
dump($validate->getError());
}
如果输入的是蜡笔小新,因为username名重复,则报错
array:1 [
"username" => "username已存在"
]
单个验证和注解验证
静态调用,即使用facade 模式进行调用验证,非常适合单个数据的验证;引入facade 中的Validate 时,和其它Validate 会冲突,要特别注意;
use think\facade\Validate;
dump( Validate::isEmail('xiaoxin@163.com') );
验证正确返回true,验证失败返回false
这个方法是tp5遗留下来的方法,随着以后版本更迭,可能会被删掉。
//验证是否为空
dump(Validate::isRequre(''));
//验证是否为数值
dump(Validate::isNumber(10));
静态调用返回的结果是false 和true,方便你进行条件判断;
静态调用,也是支持多规则验证的,使用checkRule()方法实现;
// 添加多个验证规则,是不是整数并且在1~10之间
dump( Validate::checkRule(110, 'number|between:1,10') );
另一种写法
dump( Validate::checkRule(10, ValidateRule::isNumber()->between('1, 10')) );
checkRule()不支持错误信息,需要自己实现,但支持对象化规则定义;
注解验证
可以结合之前课程中注解路由的传参,使用验证方式,对其进行验证;
# 注解验证必须引用下面两个命名空间
use think\annotation\Route;
// 由于这里与门面模式冲突了 所以这里要使用别名
use think\annotation\route\Validate as V;
/**
* @param $id
* @return string
* @route("vr/:id")
* @Validate(User::class)
*/