somnambulist / validation
对rakit/validation的重写,提供类似于Laravel的验证功能作为独立的库
Requires
- php: >=8.1
- ext-mbstring: *
Requires (Dev)
- doctrine/dbal: ^3.1.0
- phpunit/phpunit: ^10.5
- ramsey/uuid: ^4.3
- symfony/var-dumper: ^6.0
README
这是对rakit/validation的重写,一个类似于Laravel验证的独立验证器。与rakit/validation一样,这个库在使用时没有任何其他依赖。
请注意,内部API与rakit/validation有很大不同。
跳转到规则
要求
- PHP 8.0+
- ext/mb-string
安装
使用composer安装,或者从github.com检出/pull文件。
- composer require somnambulist/validation
使用方法
使用这个库验证数据有两种方式:使用make
创建验证对象,然后使用validate
验证;或者直接使用validate
。
例如
使用make
<?php require('vendor/autoload.php'); use Somnambulist\Components\Validation\Factory; $validation = (new Factory)->make($_POST + $_FILES, [ 'name' => 'required', 'email' => 'required|email', 'password' => 'required|min:6', 'confirm_password' => 'required|same:password', 'avatar' => 'required|uploaded_file:0,500K,png,jpeg', 'skills' => 'array', 'skills.*.id' => 'required|numeric', 'skills.*.percentage' => 'required|numeric' ]); $validation->validate(); if ($validation->fails()) { // handling errors $errors = $validation->errors(); echo "<pre>"; print_r($errors->firstOfAll()); echo "</pre>"; exit; } else { // validation passes echo "Success!"; }
或通过validate
<?php require('vendor/autoload.php'); use Somnambulist\Components\Validation\Factory; $validation = (new Factory)->validate($_POST + $_FILES, [ 'name' => 'required', 'email' => 'required|email', 'password' => 'required|min:6', 'confirm_password' => 'required|same:password', 'avatar' => 'required|uploaded_file:0,500K,png,jpeg', 'skills' => 'array', 'skills.*.id' => 'required|numeric', 'skills.*.percentage' => 'required|numeric' ]); if ($validation->fails()) { // handling errors $errors = $validation->errors(); echo "<pre>"; print_r($errors->firstOfAll()); echo "</pre>"; exit; } else { // validation passes echo "Success!"; }
强烈建议使用依赖注入容器并将
Factory
作为单例存储,而不是创建新实例。这将减少创建验证实例的代价,并使自定义规则更容易管理。
属性别名
与rakit/validation
不同,属性名称不会被任何方式转换;相反,如果您想命名属性,则必须使用别名。
别名可以通过几种方式定义:在规则本身中,或通过在验证中添加别名。请注意,别名应该在调用validate
之前设置。
use Somnambulist\Components\Validation\Factory; $validation = (new Factory)->make([ 'province_id' => $_POST['province_id'], 'district_id' => $_POST['district_id'] ], [ 'province_id:Province' => 'required|numeric', 'district_id:District' => 'required|numeric' ]); // or set the aliases: $validation->setAlias('province_id', 'Province'); $validation->setAlias('district_id', 'District'); // then validate it $validation->validate();
验证、有效和无效数据
验证后,数据结果保留在每个验证实例中。例如
use Somnambulist\Components\Validation\Factory; $validation = (new Factory())->validate([ 'title' => 'Lorem Ipsum', 'body' => 'Lorem ipsum dolor sit amet ...', 'published' => null, 'something' => '-invalid-' ], [ 'title' => 'required', 'body' => 'required', 'published' => 'default:1|required|in:0,1', 'something' => 'required|numeric' ]);
现在您可以获取验证后的数据,只有有效数据,或者只有无效数据
$validatedData = $validation->getValidatedData(); // [ // 'title' => 'Lorem Ipsum', // 'body' => 'Lorem ipsum dolor sit amet ...', // 'published' => '1' // notice this // 'something' => '-invalid-' // ] $validData = $validation->getValidData(); // [ // 'title' => 'Lorem Ipsum', // 'body' => 'Lorem ipsum dolor sit amet ...', // 'published' => '1' // ] $invalidData = $validation->getInvalidData(); // [ // 'something' => '-invalid-' // ]
可用规则
点击显示详情。
accepted
此规则下的字段必须是'on'
、'yes'
、'1'
、'true'
(字符串"true")或true
之一。
after:tomorrow
此规则下的字段必须是一个在给定最小值之后的日期。
参数应该是任何可以被strtotime
解析的有效字符串。例如
- after:next week
- after:2016-12-31
- after:2016
- after:2016-12-31 09:56:02
alpha
此规则下的字段必须是全部字母字符。
alpha_num
此规则下的字段必须是全部字母数字字符。
alpha_dash
此规则下的字段可以包含字母数字字符,以及破折号和下划线。
alpha_spaces
此规则下的字段可以包含字母字符,以及空格。
any_of:value,value,value
in
的一种变体:这里的值(默认用逗号分隔)必须都在给定的值中。例如:order => 'name,date'
与规则any_of:name,id
将失败验证,因为date
不是允许的值之一。分隔符可以通过在规则实例上调用separator()
来更改。
use Somnambulist\Components\Validation\Factory; use Somnambulist\Components\Validation\Rules\AnyOf; $validation = $factory->validate([ 'field' => 'foo;bar;example' ], [ 'field' => $factory->rule('any_of')->separator(';')->values(['foo', 'bar']), ]); $validation->passes(); // true if field only contains the values in any_of
与in
一样,可以通过在规则上调用->strict(true)
来执行严格的匹配。
此规则对于允许以逗号分隔数据作为单个参数的API很有用,例如JsonAPI include、order等。如果源已经是数组,则可以使用array|in:...
代替。
array
此规则下的字段必须是一个数组。
array_must_have_keys:value,value,value
数组必须包含所有指定的键才能有效。这有助于确保嵌套数组符合规定的格式。通过使用每个键的单独规则与required
也可以达到相同的效果。注意,这仍然允许存在额外的键,它只是验证特定键的存在。
此规则最好与array
规则一起使用,尽管它也可以单独使用。
use Somnambulist\Components\Validation\Factory; $validation = $factory->validate([ 'filters' => ['foo' => 'bar', 'baz' => 'example'] ], [ 'filters' => 'array|array_must_have_keys:foo,bar,baz', ]); $validation->passes(); // true if filters has all the keys in array_must_have_keys
以下示例功能等效
use Somnambulist\Components\Validation\Factory; $validation = $factory->validate([ 'filters' => ['foo' => 'bar', 'baz' => 'example'] ], [ 'filters' => 'array|array_must_have_keys:foo,bar,baz', 'filters.foo' => 'string|between:1,50', 'filters.bar' => 'numeric|min:1', 'filters.baz' => 'uuid', ]); $validation = $factory->validate([ 'filters' => ['foo' => 'bar', 'baz' => 'example'] ], [ 'filters' => 'array', 'filters.foo' => 'required|string|between:1,50', 'filters.bar' => 'required|numeric|min:1', 'filters.baz' => 'required|uuid', ]);
array_can_only_have_keys:value,value,value
数组只能包含指定的键,任何不存在的键都会使验证失败。默认情况下,关联数据对键与值没有限制。例如:您有一个搜索框的过滤器传递给SQL,只有指定的键应该允许发送,而不仅仅是过滤器的数组中的任何值。
此规则最好与array
规则一起使用,尽管它也可以单独使用。
use Somnambulist\Components\Validation\Factory; $validation = $factory->validate([ 'filters' => ['foo' => 'bar', 'baz' => 'example'] ], [ 'filters' => 'array|array_can_only_have_keys:foo,bar', ]); $validation->passes(); // true if filters only has the keys in array_can_only_have_keys
between:min,max
此规则下的字段的大小必须在min和max参数之间。值大小以与min
和max
规则相同的方式计算。
您还可以使用此规则验证上传文件的大小。
$validation = $validator->validate([ 'photo' => $_FILES['photo'] ], [ 'photo' => 'required|between:1M,2M' ]);
布尔型
此规则下的字段必须是布尔型。接受的输入有true
、false
、1
、0
、"1"
和"0"
。
回调
定义一个自定义回调来验证值。此规则不能使用字符串语法注册。要使用此规则,您必须使用数组语法,并明确指定callback
,或者传递闭包。
$validation = $validator->validate($_POST, [ 'even_number' => [ 'required', function ($value) { // false = invalid return (is_numeric($value) AND $value % 2 === 0); }, 'callback' => fn ($v) => is_numeric($v) && $v % 2 === 0, ] ]);
您可以通过返回一个字符串来设置自定义消息,而不是返回false。为了允许消息翻译,而不是返回一个字面字符串,返回一个消息密钥,并将其添加到Factory的消息包中。
注意:返回消息字符串将在未来的版本中删除,需要只返回布尔响应。相反,直接在返回true/false之前设置消息字符串,通过
$this->message = "";
。
$validation = $validator->validate($_POST, [ 'even_number' => [ 'required', function ($value) { if (!is_numeric($value)) { return ":attribute must be numeric."; } if ($value % 2 !== 0) { return ":attribute is not even number."; } return true; // always return true if validation passes } ] ]);
注意:回调闭包绑定到规则实例,允许通过$this访问规则属性。
date:format
此规则下的字段必须是符合给定格式的有效日期。参数format
是可选的,默认格式是Y-m-d
。
默认值/默认值
如果属性没有值,则此默认值将在验证数据中替换使用。
例如,如果您有如下验证
use Somnambulist\Components\Validation\Factory; $validation = (new Factory)->validate([ 'enabled' => null ], [ 'enabled' => 'default:1|required|in:0,1' 'published' => 'default:0|required|in:0,1' ]); $validation->passes(); // true // Get the valid/default data $valid_data = $validation->getValidData(); $enabled = $valid_data['enabled']; $published = $valid_data['published'];
验证通过,因为enabled
和published
的默认值设置为1
和0
,这是有效的。
different:another_field
与same
相反;此规则下的字段值必须与another_field
值不同。
digits:value
验证字段必须是数字,并且必须具有与value
精确相同的长度。
digits_between:min,max
验证字段必须是数字,并且其长度必须在给定的min
和max
之间。
ends_with:another_field
此验证字段必须以another_field
结束。比较可以是字符串、数字或数组元素。
exists:table,column (数据库)
此验证字段必须存在于给定的表中。这并不检查唯一性,只确保表中至少有一条记录包含所提供的值和列。
要使用此规则,您必须提供一个DBAL连接。这应该通过依赖注入来完成。
例如
use Somnambulist\Components\Validation\Factory; $validation = (new Factory)->validate([ 'country' => 'GBR' ], [ 'country' => 'exists:countries,id', ]); $validation->passes(); // true if table countries has a record with id GBR
为了进行更精细的验证,可以通过调用->where()
来设置闭包,以修改底层查询。闭包将传递一个Doctrine\DBAL\Query\QueryBuilder
实例。
use Doctrine\DBAL\Query\QueryBuilder; use Somnambulist\Components\Validation\Factory; use Somnambulist\Components\Validation\Rules\Exists; $factory = new Factory; $factory->addRule('exists', new Exists($dbalConn)); $validation = $factory->validate([ 'country' => 'GBR' ], [ 'country' => $factory->rule('exists')->table('countries')->column('id')->where(fn (QueryBuilder $qb) => $qb->andWhere('active = 1')), ]); $validation->passes(); // true if table countries has a record with id GBR and it is active
扩展:extension_a,extension_b,...
此规则下的字段必须以其中之一列出的扩展名结尾。
这对于验证给定路径或URL的文件类型非常有用。应使用mimes
规则来验证上传文件。
如果您需要严格的MIME检查,应实现一个自定义的
MimeTypeGuesser
,它可以利用服务器端文件检查器,该检查器使用MIME库。
浮点数
此规则下的字段必须是浮点数,例如:0.0 12.3456等。值可以是一个包含浮点数的字符串。请注意,整数和0(零)将使用此规则进行验证失败。
在:value_1,value_2,...
此规则下的字段必须包含在给定的值列表中。
为了帮助构建字符串规则,In
(和NotIn
)规则有一个辅助方法
use Somnambulist\Components\Validation\Factory; use Somnambulist\Components\Validation\Rules\In; $factory = new Factory(); $validation = $factory->validate($data, [ 'enabled' => [ 'required', In::make([true, 1]) ] ]);
此规则使用in_array
进行验证,默认情况下不执行严格检查。如果您需要严格检查,可以像这样调用规则
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $validation = $factory->validate($data, [ 'enabled' => [ 'required', $factory->rule('in')->values([true, 1])->strict() ] ]);
然后'enabled'值应该是布尔值true
或整数1
。
整数
正在验证的字段必须是整数。
IP
此规则下的字段必须是有效的IPv4或IPv6地址。
IPv4
此规则下的字段必须是有效的IPv4地址。
IPv6
此规则下的字段必须是有效的IPv6地址。
JSON
此验证字段必须是有效的JSON字符串。
长度:number
此验证字段必须是长度恰好指定的字符串。
小写
此验证字段必须为小写。
最大值:number
此规则下的字段的大小必须小于或等于给定的数字。值大小以与min
规则相同的方式计算。
您还可以使用此规则验证上传文件的最大大小。
$validation = $validator->validate([ 'photo' => $_FILES['photo'] ], [ 'photo' => 'required|max:2M' ]);
mimes:extension_a,extension_b,...
正在验证的$_FILES
项必须有一个与列出的扩展名对应的MIME类型。
此方法基于文件扩展名,而不是客户端发送的标头或嵌入式文件类型。如果您需要严格的MIME类型验证,建议实现一个自定义的
MimeTypeGuesser
,它使用完整的MIME类型查找库,并替换内置的MIME规则。
可以通过依赖注入向现有的猜测器添加额外的MIME类型,并保持MIME类型猜测器作为服务。
最小值:number
此规则下的字段的大小必须大于或等于给定的数字。
对于字符串值,大小对应于字符数。对于整数或浮点值,大小对应于其数值。对于数组,大小对应于数组的计数。如果您的值是数字字符串,您可以使用numeric
规则将其大小视为数值,而不是字符数。
您还可以使用此规则验证上传文件的最小大小。
$validation = $validator->validate([ 'photo' => $_FILES['photo'] ], [ 'photo' => 'required|min:1M' ]);
不在其中:value_1,value_2,...
此规则下的字段不得包含在给定的值列表中。
此规则也使用 in_array
,并且可以通过与 In
相同的方式启用严格检查。
可空
此规则下的字段可能为空。
数值
此规则下的字段必须是数值。
存在
此规则下的字段必须属于输入集,无论值是什么。
禁止
此规则下的字段不允许。
禁止_if
如果 another_field
提供了任何值,则此规则下的字段不允许。
禁止_unless
除非 another_field
有这些值之一,否则此规则下的字段不允许。这是 禁止_if
的逆操作。
正则表达式:/your-regex/
此规则下的字段必须匹配给定的正则表达式。注意:如果您需要使用 |
,则正则表达式规则必须以数组格式编写,而不是字符串。例如
use Somnambulist\Components\Validation\Factory; $validation = (new Factory())->validate([ 'field' => 'value' ], [ 'field' => [ 'required', 'regex' => '/(this|that|value)/' ] ])
拒绝
此规则下的字段必须有一个值与拒绝相对应,即 0(零)、"0"、false、no、"false"、off。这是 接受
规则的逆操作。
必填
此验证下的字段必须存在并且不为 '空'。
以下是一些示例
对于上传的文件,$_FILES['key']['error']
必须不是 UPLOAD_ERR_NO_FILE
。
必填_if:another_field,value_1,value_2,...
如果 another_field
字段等于任何值,则此规则下的字段必须存在并且不为空。
例如 必填_if:something,1,yes,on
将在 something
的值是 1
、'1'
、'yes'
或 'on'
之一时生效。
必填_unless:another_field,value_1,value_2,...
除非 another_field
字段等于任何值,否则此验证下的字段必须存在并且不为空。
requires:field_1,field_2,...
此验证下的字段要求指定的字段在输入数据中存在并且不为空。
例如:字段 b "requires:a";如果 a 不是一个存在的值,或者具有一个 "empty" 值,那么验证将失败。"empty" 是 false、空字符串或 null。
这是 required_with
的扩展,然而,当与 sometimes
或 nullable
一起使用时,规则将失败。例如:如果 b "requires:a" 并且 "a" 被允许为 nullable
,b 将失败,因为它明确要求具有值的 a。
required_with:field_1,field_2,...
仅当其他指定的字段之一存在时,此验证下的字段必须存在并且不为空。
注意:如果定义此规则的规则是 sometimes
或 nullable
,则此规则的行为可能会被规避。例如:如果 a 是 "required_with:b",但 a 也只是 sometimes
存在,那么 required_with 将永远不会触发,因为 sometimes 规则会否定它。a 还需要显式传递以触发规则。
required_without:field_1,field_2,...
仅当其他指定的字段之一不存在时,此验证下的字段必须存在并且不为空。
required_with_all:field_1,field_2,...
仅当所有其他指定的字段都存在时,此验证下的字段必须存在并且不为空。
required_without_all:field_1,field_2,...
仅当所有其他指定的字段都不存在时,此验证下的字段必须存在并且不为空。
same:another_field
此规则下的字段值必须与 another_field
具有相同的值。
sometimes
如果字段存在于输入数据中,则应该对字段进行验证。例如:field => sometimes|required|email
starts_with:another_field
此验证下的字段必须以 another_field
开头。比较可以是字符串、数字和数组元素。
字符串
根据此规则,该字段必须是PHP字符串。
唯一:表,列,忽略,忽略列(数据库)
此验证下的字段必须在给定的表中是唯一的。可选:可以忽略一个值,如果提供了ignore_column,这可以是一个替代列的值。
要使用此规则,您必须提供一个DBAL连接。这应该通过依赖注入来完成。
例如
use Somnambulist\Components\Validation\Factory; $validation = (new Factory)->validate([ 'email' => 'foo@example.org' ], [ 'email' => 'email|unique:users,email', ]); $validation->passes(); // true if table users does not contain the email
忽略当前用户的电子邮件地址
use Somnambulist\Components\Validation\Factory; $validation = (new Factory)->validate([ 'email' => 'foo@example.org' ], [ 'email' => 'email|unique:users,email,10,id', ]); $validation->passes(); // true if table users ignoring id 10, does not contain email
为了进行更精细的验证,可以通过调用->where()
来设置闭包,以修改底层查询。闭包将传递一个Doctrine\DBAL\Query\QueryBuilder
实例。
use Doctrine\DBAL\Query\QueryBuilder; use Somnambulist\Components\Validation\Factory; use Somnambulist\Components\Validation\Rules\Unique; $factory = new Factory; $factory->addRule('unique', new Unique($dbalConn)); $validation = $factory->validate([ 'email' => 'foo@example.org' ], [ 'email' => $factory->rule('unique')->table('users')->column('email')->where(fn (QueryBuilder $qb) => $qb->andWhere('active = 1')), ]); $validation->passes(); // true if table users does not contain an active email
上传文件:min_size,max_size,extension_a,extension_b,...
此规则将验证来自 $_FILES
的数据。此规则下的字段有以下条件
$_FILES['key']['error']
必须是UPLOAD_ERR_OK
或UPLOAD_ERR_NO_FILE
。对于UPLOAD_ERR_NO_FILE
,您可以使用required
规则进行验证。- 如果提供了最小大小,上传文件的大小 不能低于 最小大小。
- 如果提供了最大大小,上传文件的大小 不能高于 最大大小。
- 如果提供了文件类型,则MIME类型必须是给定类型之一。
当使用字符串定义时,必须同时提供大小限制。要指定只有最大大小,请使用工厂方法获取规则并使用方法链。
以下是一些示例定义和说明
uploaded_file
:上传文件是可选的。当它不为空时,它必须是ERR_UPLOAD_OK
。required|uploaded_file
:上传文件是必需的,并且必须是ERR_UPLOAD_OK
。uploaded_file:0,1M
:上传文件大小必须在0 - 1 MB之间,但上传文件是可选的。required|uploaded_file:0,1M,png,jpeg
:上传文件大小必须在0 - 1MB之间,并且MIME类型必须是image/jpeg
或image/png
。
对于多个文件上传,PHP使用格式 _FILES[key][name][0..n+1]
(更多详情请参阅PHP手册)。如果使用点表示法提供属性键,则文件数组将自动重新排序为相关属性的嵌套数组。这允许使用相同的规则验证多个文件。这仅在属性名使用点表示法时才会发生。
从1.4.2版本开始,不使用点规则允许多个文件将引发 RuntimeException
。
例如,如果您有如下输入文件
<input type="file" name="photos[]"/> <input type="file" name="photos[]"/> <input type="file" name="photos[]"/>
您可以使用以下方式验证所有文件
$validation = (new Factory)->validate($_FILES, [ 'photos.*' => 'uploaded_file:0,2M,jpeg,png' ]); // or $validation = (new Factory)->validate($_FILES, [ 'photos.*' => 'uploaded_file|max:2M|mimes:jpeg,png' ]);
或者如果您有如下输入文件
<input type="file" name="images[profile]"/> <input type="file" name="images[cover]"/>
您可以如此验证它
$validation = (new Factory)->validate($_FILES, [ 'images.*' => 'uploaded_file|max:2M|mimes:jpeg,png', ]); // or $validation = (new Factory)->validate($_FILES, [ 'images.profile' => 'uploaded_file|max:2M|mimes:jpeg,png', 'images.cover' => 'uploaded_file|max:5M|mimes:jpeg,png', ]);
大写
此验证下的字段必须是大写。
URL
根据此规则,该字段必须是有效的URL格式。默认情况下,验证常见的格式:any_scheme://...
。如果您愿意,可以指定特定的URL方案。
例如
$validation = (new Factory)->validate($inputs, [ 'random_url' => 'url', // value can be `any_scheme://...` 'https_url' => 'url:http', // value must be started with `https://` 'http_url' => 'url:http,https', // value must be started with `http://` or `https://` 'ftp_url' => 'url:ftp', // value must be started with `ftp://` 'custom_url' => 'url:custom', // value must be started with `custom://` ]);
与
rakit
、mailto和JDBC不同,不支持。请实现自定义规则或正则表达式来验证这些。
UUID
此验证下的字段必须是有效的UUID,而不是nil UUID字符串。
可选验证与空值验证
有时属性可以省略或可以是null。这些情况应谨慎处理,并且验证后的结果可能不同。
对于可选属性,可以在验证数据中省略,即仅在数据存在时进行验证,可以使用 sometimes
规则。如果指定了此规则,则该属性可以完全省略,或者必须满足验证标准。这对于像搜索过滤器或不是始终必需的分页标记等非常有用。
[ 'filters' => 'sometimes|array', ]
在此示例中 filters
完全可选,但如果指定了,则应该是值的数组。传递 [filters => '']
将不是有效的,它必须是: [filters => []]
。
有时,属性不是可选的,而是应该是未定义的,即 null
。通常,使用 sometimes
并省略值更可取,但可能有必要保持具有 null
值的属性。在这些情况下,使用 nullable
规则。这将允许属性存在而不带任何值。例如:用户的生日可能是可空的或日期: nullable|date
。
与rakit/validation
不同,使用可空数据可能会引起问题,因为这个库在整个过程中都使用严格的类型。这意味着许多测试字符串、数组或数字错误的规则,因为它们接收到了null
。这是规则定义过程中的一个歧义。例如,规则name: string|max:200
隐式地意味着name
应该是一个字符串,最多200个字符——《null》不应有效,但为了保持部分兼容性,它将允许null
。
这个库的下一个主要版本将移除这种处理,并使这种类型的定义必须字段存在且值不为空(除非显式允许为空)。要允许空值,需要显式定义nullable
规则。因此,始终使用可空或有时是良好的实践。
验证数组数据
该库可以通过使用点符号来定义数组结构来验证复杂的数据数组。有一些变体和一些边缘情况需要注意,以防止问题。
最常见的情况是想要允许一个类似于本说明文档中早期示例的选项数组。
[ 'skills' => 'array', 'skills.*.id' => 'required|numeric', 'skills.*.percentage' => 'required|numeric' ],
早期的规则是设置为验证与用户相关的数据,包括技能数组。每个技能都有一个id和一个百分比值。在这种情况下,父键skills
应该定义规则array
,这是为了确保数据实际上是一个数组。然后使用*
引用每个技能属性,以表示技能属性中有多个值。
这些规则将验证以下数组结构
[ 'skills' => [ [ 'id' => 3, 'percentage' => 50, ], [ 'id' => 17, 'percentage' => 50, ], ] ]
较少见的情况是没有父键的数组数组。在这种情况下,没有前缀,每个子键以*
开头。在这种情况下,你应该小心不要将标准键-值对与数组数据混合。
例如
[ '*.id' => 'required|numeric', '*.percentage' => 'required|numeric' ]
将用于验证以下数组结构
[ [ 'id' => 3, 'percentage' => 50, ], [ 'id' => 17, 'percentage' => 50, ], ]
为了避免问题,你需要确保数据不会包括
[ 'name' => 'foo bar', [ 'id' => 3, 'percentage' => 50, ], [ 'id' => 17, 'percentage' => 50, ], ]
依赖验证规则和数组数据
某些规则用于确定某些键的存在或是否需要。通常这些使用标准键名,例如:confirm_password
应与password
字段相同,所以规则写为:same:password
。
然而,对于数组数据,这不会工作,因为属性不是属性名,而是该属性的路径。
以相同的技能数组为例,假设我们想要在技能为新时要求标签。如果指定为required_if:id:null
,那么验证将查找数据根中的属性名id
,但它不存在,或者它可能找到错误的键。
相反,我们必须将规则显式绑定到相同的技能键,规则写为:required_if:skills.*.id,null
。如果不这样做,则规则将被忽略或失败。同样,当使用数组数组时,在数组中引用其他字段时应该以*
为前缀,例如:required_if:*.id,null
。
以下为两种语法的示例
[ 'skills.*.id' => 'sometimes|numeric', 'skills.*.percentage' => 'required|numeric', 'skills.*.title' => 'required_if:skills.*.id,null|string', ]
数组数组
[ '*.id' => 'sometimes|numeric', '*.percentage' => 'required|numeric', '*.title' => 'required_if:*.id,null|string', ]
验证消息
验证消息定义在Resources/i18n/en.php
中。任何消息都可以替换为自定义字符串,或翻译为其他语言。英文字符串始终在Factory
实例化时加载。
根据失败类型,将提供各种变量以供使用,但是以下变量始终可用于所有消息
:attribute
:正在验证的属性,如果设置了别名,则使用别名,:value
:正在验证的属性的值,已转换为字符串,数组对象作为JSON字符串。
加载翻译消息
默认情况下,只有英文消息由Factory
类加载。在编写此文档时,已经由贡献者提供了德语翻译,但是可以通过创建一个返回包含消息键和新的消息字符串数组的PHP文件来添加任何语言。
要加载内置语言,必须在调用validate之前调用Factor::registerLanguageMessages()
。例如:
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->registerLanguageMessages('de');
registerLanguageMessages
有一个可选的第二个参数,允许指定语言文件的路径。如果没有提供,则将使用<vendor_dir>/src/Resources/i18n
的库路径。如果您希望使用完全自定义的语言文件,请使用第二个参数提供您的文件。这可以是一个完全覆盖默认消息的英文语言文件。
例如
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->registerLanguageMessages('en', '/path/to/project/i18n/en_US.php');
可以多次调用以添加多种语言。
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->registerLanguageMessages('en', '/path/to/project/i18n/en_US.php'); $factory->registerLanguageMessages('es', '/path/to/project/i18n/es.php'); $factory->registerLanguageMessages('de', '/path/to/project/i18n/de.php');
Validator的自定义消息
所有消息都存储在Factory
实例的MessageBag
中。可以向该消息包添加更多语言,或在特定的验证实例上进行自定义。此外,可以在Factory的消息包上设置默认语言,或在验证实例上设置特定语言。
要添加一组新的消息:
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->messages()->add('es', [ 'rule.required' => 'Se requiere :attribute', ]); $validation = $factory->validate($inputs, $rules); $validation->setLanguage('es')->validate();
或覆盖默认的英文字符串:
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->messages()->replace('en', 'rule.required', 'Se requiere :attribute'); $validation = $factory->validate($inputs, $rules); $validation->validate();
或设置默认语言:
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->messages()->default('es'); $validation = $factory->validate($inputs, $rules); $validation->validate();
特定属性规则的自定义消息
有时您可能想要为特定属性规则设置自定义消息,以使其更明确或添加其他信息。这可以通过为属性添加带有:
和规则名称的消息键来完成。
例如
use Somnambulist\Components\Validation\Factory; $validator = new Factory(); $validation_a = $validator->make($input, [ 'age' => 'required|min:18' ]); $validation->messages()->add('en', 'age:min', '18+ only'); $validation->validate();
有时您可能希望在使用错误消息时使用来自其他规则的参数。从版本1.6.0开始,您可以使用点符号来访问这些参数,然后是您想要使用的参数。例如:
一个password
属性使用required|between:8,16|regex:/^[\\da-zA-Z!$%+.]+$/
进行验证,但错误消息总是想引用最小/最大值。这可以这样完成:
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->messages()->replace('en', 'password:between', 'Your password must be between :min and :max characters and only [! $ % + .] as special characters.'); $factory->messages()->replace('en', 'password:regex', 'Your password must be between :between.min and :between.max characters and only [! $ % + .] as special characters.');
对于regex
消息,通过在最小/最大值前加上between.
来引用between
的参数。并非所有规则都有参数,在这些情况下不会进行替换。
请注意,只能引用同一属性的规则参数。您不能访问完全不同的属性中的参数,例如:如果您验证了电子邮件或用户名,您将无法在密码上下文中访问这些参数。
规则的自定义消息
一些规则有几个可能的验证消息。它们都命名为rule.<name>.<check>
。要更改消息,可以覆盖或添加特定的消息。
例如,uploaded_file
可能对文件、最小/最大大小和类型有失败。这些都绑定到:
- rule.uploaded_file
- rule.uploaded_file.min_size
- rule.uploaded_file.max_size
- rule.uploaded_file.type
要更改任何子消息,请向消息包添加/覆盖该消息键。
例如
use Somnambulist\Components\Validation\Factory; $validator = new Factory(); $validation_a = $validator->make($input, [ 'age' => 'required|min:18' ]); $validation->messages()->add('en', 'age:min', '18+ only'); $validation->validate();
与
rakit
不同,不能直接在Rule
实例中设置自定义消息。任何消息都必须在消息包中设置。
复杂的翻译需求
此库中的翻译系统相当基础。如果您有复杂的需求,或希望处理可数等,那么所有错误消息都存储为包含消息键和该消息变量的ErrorMessage
实例。
您可以使用底层的数组(或一个DataBag
实例)来显示消息,而不是使用ErrorBag
,然后传递消息键和变量到您的翻译系统中。
请注意,错误是按属性和规则名称嵌套的集合。
处理错误消息
错误消息收集在您可以通过验证实例上的errors()
访问的ErrorBag
实例中。
use Somnambulist\Components\Validation\Factory; $validation = (new Factory())->validate($inputs, $rules); $errors = $validation->errors();
现在,您可以使用以下方法检索消息:
all(string $format = ':message')
获取所有消息作为平铺数组。
$messages = $errors->all(); // [ // 'email is not a valid email address', // 'password minimum is 6 characters', // 'password must contain capital letters' // ] $messages = $errors->all('<li>:message</li>'); // [ // '<li>email is not a valid email address</li>', // '<li>password minimum is 6 character</li>', // '<li>password must contain capital letters</li>' // ]
首先(string $format = ':message', bool $dotNotation = false)
从所有现有键中获取第一条消息
$messages = $errors->firstOfAll(); // [ // 'email' => 'Email is not valid email', // 'password' => 'Password minimum 6 character', // ] $messages = $errors->firstOfAll('<li>:message</li>'); // [ // 'email' => '<li>Email is not valid email</li>', // 'password' => '<li>Password minimum 6 character</li>', // ]
参数$dotNotation
用于数组验证。如果它是false
,则返回原始数组结构;如果它是true
,则返回带有点表示法键的扁平化数组。
例如
$messages = $errors->firstOfAll(':message', false); // [ // 'contacts' => [ // 1 => [ // 'email' => 'Email is not valid email', // 'phone' => 'Phone is not valid phone number' // ], // ], // ] $messages = $errors->firstOfAll(':message', true); // [ // 'contacts.1.email' => 'Email is not valid email', // 'contacts.1.phone' => 'Email is not valid phone number', // ]
first(string $key)
获取给定键的第一个消息。如果键有任何错误消息,则返回string
;如果没有错误,则返回null
。
例如
if ($emailError = $errors->first('email')) { echo $emailError; }
toArray()
获取ErrorMessage对象的原始底层关联数组。
例如
$messages = $errors->toArray(); // [ // 'email' => [ // 'email' => 'Email is not valid email' // ], // 'password' => [ // 'min' => 'Password minimum 6 character', // 'regex' => Password must contains capital letters' // ] // ]
toDataBag()
将ErrorMessage对象的原始底层关联数组作为DataBag
实例获取。
例如
$message = $errors->toDataBag()->filter()->first();
count()
获取错误消息的数量。
has(string $key)
检查给定的键是否有错误。如果键有错误,则返回true
,否则返回false
。
注册/覆盖规则
默认情况下,所有内置规则都会自动注册到Factory
实例。其中一些规则是内部需要的(例如required
和callback
);然而,你可以为验证添加任意数量的新规则到工厂中。
这是通过访问Factory
实例上的addRule()
方法并添加一个新的规则实例来完成的。
例如,你想创建一个检查数据库中字段可用性的unique
验证器。
首先,让我们创建UniqueRule
类
<?php declare(strict_types=1); use Somnambulist\Components\Validation\Rule; class UniqueRule extends Rule { protected string $message = ":attribute :value has been used"; protected array $fillableParams = ['table', 'column', 'except']; protected PDO $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function check($value): bool { // make sure required parameters exists $this->assertHasRequiredParameters(['table', 'column']); // getting parameters $column = $this->parameter('column'); $table = $this->parameter('table'); $except = $this->parameter('except'); if ($except && $except == $value) { return true; } // do query $stmt = $this->pdo->prepare(sprintf('select count(*) as count from %s where %s = :value', $table, $column)); $stmt->bindParam(':value', $value); $stmt->execute(); $data = $stmt->fetch(PDO::FETCH_ASSOC); // true for valid, false for invalid return intval($data['count']) === 0; } }
现在要将此规则注册到Factory
实例,需要将其添加到其中
use Somnambulist\Components\Validation\Factory; $factory = new Factory(); $factory->addRule('unique', new UniqueRule($pdo));
现在你可以这样使用它
$validation = $factory->validate($_POST, [ 'email' => 'email|unique:users,email,exception@mail.com' ]);
在上面的UniqueRule
中,属性$message
用于无效消息。属性$fillableParams
定义了规则参数的顺序和名称。默认情况下,fillParameters
将从字符串规则填充列在$fillableParams
中的参数。例如,上面的例子中的unique:users,email,exception@mail.com
将设置
$params['table'] = 'users'; $params['column'] = 'email'; $params['except'] = 'exception@mail.com';
如果你想你的自定义规则接受参数列表,如
in
、not_in
或uploaded_file
规则,你需要在你的自定义规则类中重写fillParameters(array $params)
方法。
请注意,我们上面创建的unique
规则也可以这样使用
$validation = $factory->validate($_POST, [ 'email' => [ 'required', 'email', $factory('unique', 'users', 'email') ] ]);
你可以通过添加一些方法来改进上面的UniqueRule
类,而不是使用字符串格式来设置参数
<?php class UniqueRule extends Rule { public function table(string $table): self { $this->params['table'] = $table; return $this; } public function column(string $column): self { $this->params['column'] = $column; return $this; } public function except(string $value): self { $this->params['except'] = $value; return $this; } }
现在配置规则变为
$validation = $factory->validate($_POST, [ 'email' => [ 'required', 'email', $validator('unique')->table('users')->column('email')->except('exception@mail.com') ] ]);
隐式规则
隐式规则是一种规则,如果它无效,则忽略后续规则。例如,如果属性没有通过required*
规则,则后续规则将无效。为了防止不必要的验证和错误消息,我们将required*
规则设置为隐式。
要使你的自定义规则隐式,你可以将$implicit
属性的值设置为true
。例如
<?php use Somnambulist\Components\Validation\Rule; class YourCustomRule extends Rule { protected bool $implicit = true; }
修改值
在某些情况下,你可能希望你的自定义规则能够像default/defaults
规则一样修改属性值。在当前和下一个规则检查中,将使用你修改的值。
为此,你应该实现Somnambulist\Components\Validation\Rules\Contracts\ModifyValue
并在你的自定义规则类中创建modifyValue(mixed $value)
方法。
例如
<?php use Somnambulist\Components\Validation\Rule; use Somnambulist\Components\Validation\Rules\Contracts\ModifyValue; class YourCustomRule extends Rule implements ModifyValue { public function modifyValue(mixed $value): mixed { // Do something with $value return $value; } }
在验证之前钩子
在运行验证之前,你可能需要进行一些准备。例如,uploaded_file
规则将解决来自$_FILES
(不理想)数组的属性值,使其成为一个有组织的数组。
为此,你应该实现Somnambulist\Components\Validation\Rules\Contracts\BeforeValidate
并在你的自定义规则类中创建beforeValidate()
方法。
例如
<?php use Somnambulist\Components\Validation\Rule; use Somnambulist\Components\Validation\Rules\Contracts\BeforeValidate; class YourCustomRule extends Rule implements BeforeValidate { public function beforeValidate(): void { $attribute = $this->getAttribute(); $validation = $this->validation; // Do something with $attribute and $validation // For example change attribute value $validation->setValue($attribute->getKey(), "your custom value"); } }
测试
使用PHPUnit 9+进行测试。通过vendor/bin/phpunit
运行测试。
贡献
欢迎贡献力量!请复制仓库并提交PR。请确保您的代码使用PSR-12编码标准格式化,并且所有PHP文件在开头<?php
标签处包含declare(strict_types=1);
。如有任何代码风格规范的疑问,请查看现有文件并遵循。
该库目前针对PHP 8.0.X和8.1+。如果使用8.1函数,请确保使用合适的回退方案。注意,不要将外部库添加到该项目中。
添加新功能时,请确保更新README.md
文件以反映您的更改,并包括适当的测试。如果可能的话,还应包括语言翻译,英语为首选。
对于错误修复,必须在测试中包含失败的案例。没有适当测试或无法复制的更改可能会被拒绝。