表单验证

表单验证


表单验证

简介

Laravel 提供了几种不同的方法来验证传入应用程序的数据。默认情况下,Laravel 的控制器基类使用ValidatesRequestsTrait,它提供了一种方便的方法去使用各种强大的验证规则来验证传入的 HTTP 请求。

快速验证

要了解 Laravel 强大的验证功能,让我们看一个验证表单并将错误消息显示回给用户的完整示例。

定义路由

首先,让我们假设在routes/web.php文件中定义了下面这些路由:

Route::get('post/create', 'PostController@create'); Route::post('post', 'PostController@store');

显然,GET路由会显示一个供用户创建一个新的博客帖子的表单,而 POST 路由会将新的博客文章存储在数据库中。

创建路由器

下面让我们一起来看看处理这些路由的控制器,store方法暂时留空。


      

编写验证器逻辑

现在我们开始在store方法中编写逻辑来验证新的博客文章。为此,我们将使用Illuminate\Http\Request对象提供的validate方法 。如果验证通过,代码就可以正常的运行。如果验证失败,则会抛出异常,并自动将对应的错误响应返回给用户。在典型的 HTTP 请求的情况下,会生成一个重定向响应,而对于 AJAX 请求则会发送 JSON 响应。

让我们接着回到store方法来深入理解validate方法:

/** * 保存一篇新的博客文章。 * * @param Request $request * @return Response */ public function store(Request $request){ $validatedData = $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); // 博客文章验证通过 }

如你所见,我们将所需的验证规则传递至validate方法中。另外再提醒一次,如果验证失败,会自动生成一个对应的响应。如果验证通过,那我们的控制器将会继续正常运行。

首次验证失败后停止运行

如果你希望在某个属性第一次验证失败后停止运行验证规则,你需要附加bail规则到该属性:

$request->validate([ 'title' => 'bail|required|unique:posts|max:255', 'body' => 'required', ]);

在这个例子中,如果title字段没有通过unique规则,那么程序就不会继续检查max规则。规则会按照分配的顺序来验证。

关于数组数据的注意实现

如果你的 HTTP 请求包含一个 「嵌套」 参数(即数组),那你可以在验证规则中通过 「点」 语法来指定这些参数。

$request->validate([ 'title' => 'required|unique:posts|max:255', 'author.name' => 'required', 'author.description' => 'required', ]);

显示验证错误信息

如果传入的请求参数未通过给定的验证规则呢?正如前面所提到的,Laravel 会自动把用户重定向到之前的位置。另外,所有的验证错误信息会被自动 存储到session中。

重申一次,我们不必在GET路由中将错误消息显式绑定到视图。因为 Lavarel 会检查在 Session 数据中的错误信息,并自动将其绑定到视图(如果这个视图文件存在)。而其中的变量$errorsIlluminate\Support\MessageBag的一个实例。要获取关于这个对象的更多信息,请 查阅这个文档 。

{tip}$errors变量被Web中间件组提供的Illuminate\View\Middleware\ShareErrorsFromSession中间件绑定到视图中。当这个中间件被应用后,在你的视图中就可以获取到$error变量, 可以使一直假定$errors变量存在并且可以安全地使用。

在上面的例子中,当验证失败的时候,用户将会被重定向到控制器的create方法,使我们能在视图中显示错误信息:

 

创建文章

@if ($errors->any())
    @foreach ($errors->all() as $error)
  • {{ $error }}
  • @endforeach
@endif

关于可选字段的注意事项

默认情况下,在 Laravel 应用的全局中间件堆栈App\Http\Kernel类中包含了TrimStringsConvertEmptyStringsToNull中间件。因此,如果你不希望验证程序将null值视为无效的话,那就需要将「可选」的请求字段标记为nullable,举个例子:

$request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', 'publish_at' => 'nullable|date', ]);

在这个例子里,我们指定publish_at字段可以为null或者一个有效的日期格式。如果nullable的修饰词没有被添加到规则定义中,验证器会认为null是一个无效的日期格式。

AJAX 请求 & 验证

在这个例子中,我们使用传统的表单将数据发送到应用程序。但实际情况中,很多程序都会使用 AJAX 来发送请求。当我们对 AJAX 的请求中使用validate方法时,Laravel 并不会生成一个重定向响应,而是会生成一个包含所有验证错误信息的 JSON 响应。这个 JSON 响应会包含一个 HTTP 状态码 422 被发送出去。

验证表单请求

创建表单请求验证

面对更复杂的验证情境中,你可以创建一个「表单请求」来处理更为复杂的逻辑。表单请求是包含验证逻辑的自定义请求类。可使用 Artisan 命令make:request来创建表单请求类:

php artisan make:request StoreBlogPost

新生成的类保存在app/Http/Requests目录下。如果这个目录不存在,运行make:request命令时它会被创建出来。让我们添加一些验证规则到rules方法中:

/** * 获取适用于请求的验证规则。 * * @return array */ public function rules(){ return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; }

{tip} 你可以向rules方法传入所需的任何依赖项。他们会自动被 Laravel 提供的 服务容器 自动解析。

验证规则是如何运行的呢?你所需要做的就是在控制器方法中类型提示传入的请求。在调用控制器方法之前验证传入的表单请求,这意味着你不需要在控制器中写任何验证逻辑:

/** * 存储传入的博客文章。 * * @param StoreBlogPost $request * @return Response */ public function store(StoreBlogPost $request){ // 传入的请求通过验证... // 获取通过验证的数据... $validated = $request->validated(); }

如果验证失败,就会生成一个让用户返回到先前的位置的重定向响应。这些错误也会被闪存到session中,以便这些错误都可以在页面中显示出来。如果传入的请求是 AJAX,会向用户返回具有 422 状态代码和验证错误信息的 JSON 数据的 HTTP 响应。

添加表单请求后钩子

如果你想在表单请求「之后」添加钩子,可以使用withValidator方法。这个方法接收一个完整的验证构造器,允许你在验证结果返回之前调用任何方法:

/** * 配置验证器实例。 * * @param \Illuminate\Validation\Validator $validator * @return void */ public function withValidator($validator){ $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } }); }

表单请求授权验证

表单请求类内也包含了authorize方法。在这个方法中,你可以检查经过身份验证的用户确定其是否具有更新给定资源的权限。比方说,你可以判断用户是否拥有更新文章评论的权限:

/** * 判断用户是否有权限做出此请求。 * * @return bool */ public function authorize(){ $comment = Comment::find($this->route('comment')); return $comment && $this->user()->can('update', $comment); }

由于所有的表单请求都是继承了 Laravel 中的请求基类,所以我们可以使用user方法去获取当前认证登录的用户。同时请注意上述例子中对route方法的调用。这个方法允许你在被调用的路由上获取其定义的 URI 参数,譬如下面例子中的{comment}参数:

Route::post('comment/{comment}');

如果authorize方法返回false,则会自动返回一个包含 403 状态码的 HTTP 响应,也不会运行控制器的方法。

如果你打算在应用程序的其它部分处理授权逻辑,只需从authorize方法返回true

/** * 判断用户是否有权限进行此请求。 * * @return bool */ public function authorize(){ return true; }

{tip} 你可以向authorize方法传入所需的任何依赖项。他们会自动被 Laravel 提供的 服务容器 自动解析。

自定义错误消息

你可以通过重写表单请求的messages方法来自定义错误消息。此方法应返回属性 / 规则对数组及其对应错误消息:

/** * 获取已定义验证规则的错误消息。 * * @return array */ public function messages(){ return [ 'title.required' => 'A title is required', 'body.required' => 'A message is required', ]; }

自定义验证属性

如果你希望将验证消息的:attribute部分替换为自定义属性名称,则可以重写attributes方法来指定自定义名称。此方法应返回属性 / 名称对的数组:

/** * 获取验证错误的自定义属性。 * * @return array */ public function attributes(){ return [ 'email' => 'email address', ]; }

手动创建验证器

如果你不想在请求上使用validate方法,你可以通过Validatorfacade 手动创建一个验证器示例。
Validatorfacade 上的make方法创建一个验证器示例:

all(), [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); if ($validator->fails()) { return redirect('post/create') ->withErrors($validator) ->withInput(); } // Store the blog post... } }

传给make方法的第一个参数是需要验证的数据。第二个参数则是该数据的验证规则。

如果验证失败,则可以使用withErrors方法把错误消息闪存到 Session 。使用这个方法进行重定向后,$errors变量会自动和视图共享,你可以把这些消息显示给用户。withErrors方法接收验证器、MessageBag或 PHPArray

自动重定向

如果你想手动创建验证器实例,又想使用validates方法提供的自动重定向,那么你可以在现有的验证器示例上调用validate方法。如果验证失败,用户将会自动重定向。在 AJAX 请求中,则会返回 JSON 格式的响应。

Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ])->validate();

命名错误包

如果你一个页面中有多个表单,你可以通过命名错误包来检索特定表单的错误消息。只需给withErrors方法传递一个名字作为第二个参数

return redirect('register') ->withErrors($validator, 'login');

然后你就可以从$errors变量中获取指定表单的错误消息:

{{ $errors->login->first('email') }}

验证后钩子

验证器还允许你添加在验证成功之后允许的回调函数,以便你进行下一步的验证,甚至在消息集合中添加更多的错误消息。使用它只需在验证实例上使用after方法:

$validator = Validator::make(...);$validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } }); if ($validator->fails()) { // }

处理错误消息

Validator实例上调用errors方法后,你会得到一个Illuminate\Support\MessageBag实例,它拥有各种方便的方法处理错误信息。自动提供给所有视图的$ errors变量,也是MessageBag类的一个实例。

查看特定字段的第一个错误信息

要查看特定字段的第一个错误消息,可以使用first方法:

$errors = $validator->errors();echo $errors->first('email');

查看特定字段的所有错误消息

如果你需要获取指定字段的所有错误消息的数组,则可以使用get方法:

foreach ($errors->get('email') as $message) { // }

如果要验证表单的数组字段,你可以使用*来获取每个数组元素的所有错误消息:

foreach ($errors->get('attachments.*') as $message) { // }

查看所有字段的所有错误消息

如果你想要得到所有字段的所有错误消息,可以使用all方法:

foreach ($errors->all() as $message) { // }

判断特定字段是否含有错误消息

has方法可以被用来判断给定的字段是否存在错误信息:

if ($errors->has('email')) { // }

自定义错误消息

如果有需要的话,你也可以使用自定义错误消息取代默认值进行验证。有几种方法可以指定自定义消息。首先,你可以将自定义消息作为第三个参数传递给Validator::make方法:

$messages = [ 'required' => 'The :attribute field is required.', ]; $validator = Validator::make($input, $rules, $messages);

在这个例子中,:attribute占位符会被验证字段的实际名称取代。除此之外,你还可以在验证消息中使用其它占位符。例如:

$messages = [ 'same'=> 'The :attribute and :other must match.', 'size'=> 'The :attribute must be exactly :size.', 'between' => 'The :attribute value :input is not between :min - :max.', 'in'=> 'The :attribute must be one of the following types: :values', ];

为给定属性指定自定义消息

有时候你可能只想为特定的字段自定义错误消息。只需在属性名称后使用「点」语法来指定验证的规则即可:

$messages = [ 'email.required' => 'We need to know your e-mail address!', ];

在语言文件中指定自定义消息

在大多数情况下,您可能会在语言文件中指定自定义消息,而不是直接将它们传递给Validator。为此,需要把你的消息放置于resources/lang/xx/validation.php语言文件内的custom数组中。

'custom' => [ 'email' => [ 'required' => 'We need to know your e-mail address!', ], ],

在语言文件中指定自定义属性

如果你希望将验证消息的:attribute占位符替换为自定义属性名称,你可以在resources/lang/xx/validation.php语言文件的attributes数组中指定自定义名称:

'attributes' => [ 'email' => 'email address', ],

在语言文件中指定自定义值

有时可能需要将验证消息的:value占位符替换为值的自定义文字。例如,如果payment_type的值为cc,使用以下验证规则,该规则指定需要信用卡号:

$request->validate([ 'credit_card_number' => 'required_if:payment_type,cc' ]);

如果此验证规则失败,则会产生以下错误消息:

当payment type为cc时,credit card number 不能为空。

您可以通过定义values数组,在validation语言文件中指定自定义值表示,而不是显示cc作为支付类型值:

'values' => [ 'payment_type' => [ 'cc' => '信用卡' ], ],

现在,如果验证规则失败,它将产生以下消息:

当payment type 为信用卡时,credit card number不能为空。

可用验证规则

以下是所有可用验证规则及其功能的列表:

accepted

验证字段必须是yeson1,或true。这在确认「服务条款」是否同意时相当有用。

active_url

根据 PHP 函数dns_get_record,验证字段必须具有有效的 A 或 AAAA 记录。

after:date

验证字段必须是给定日期之后的值。日期值将传递到 PHP 函数strtotime

'start_date' => 'required|date|after:tomorrow'

您可以指定另一个要与日期进行比较的字段,而不是传递要由strtotime处理的日期字符串:

'finish_date' => 'required|date|after:start_date'

after_or_equal:date

验证字段必须是在给定日期之后或与此日期相同的值。更多信息,请参阅after规则。

alpha

验证字段必须完全由字母构成。

alpha_dash

验证字段可能包含字母、数字,以及破折号 (-) 和下划线 ( _ )。

alpha_num

验证字段必须是完全是字母、数字。

array

验证的字段必须是一个 PHP 数组。

bail

在第一次验证失败后停止运行验证规则。

before:date

验证字段必须是在给定日期之前。这个日期值将会被传递给 PHP 的strtotime函数来计算。

before_or_equal:date

验证字段必须是在给定日期之前或与之相同的日期。这个日期值将会被传递给 PHP 的strtotime函数来计算。

between:min,max

验证字段的大小必须在给定的minmax之间。字符串、数字、数组和文件的计算方式都使用size方法。

boolean

验证的字段必须可以转换为 Boolean 类型。 可接受的输入为true,false,1,0,"1", 和"0"

confirmed

验证字段必须具有匹配字段foo_confirmation。例如,验证字段为password,输入中必须存在与之匹配的password_confirmation字段。

date

根据 PHP 函数strtotime,验证字段必须是有效的日期。

date_equals:date

验证字段必须等于给定日期。日期将传递到 PHP 函数strtotime

date_format:format

验证字段必须匹配给定的日期格式。当验证某个字段的时候,你应该只使用date或者date_format,而不是同时使用。

different:field

验证字段必须具有与field不同的值。

digits:value

验证字段必须为numeric,且必须具有_value_的确切长度。

digits_between:min,max

验证的字段的长度必须在给定的minmax之间。

dimensions

验证中的文件必须是图片,并且符合指定的规则约束:

'avatar' => 'dimensions:min_width=100,min_height=200'

可用的约束为:min_width,max_width,min_height,max_height,width,height,ratio

ratio限制应该表示为宽度除以高度。 这可以通过使用像3/2的表达式 或者一个浮点数,像1.5来指定:

'avatar' => 'dimensions:ratio=3/2'

由于此规则需要多个参数,你可以使用Rule::dimensions方法流畅地构造规则:

use Illuminate\Validation\Rule; Validator::make($data, [ 'avatar' => [ 'required', Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2), ], ]);

distinct

当验证数组时,验证字段不得包含任何重复值。

'foo.*.id' => 'distinct'

email

验证字段必须为正确格式的电子邮件地址。

exists:table,column

验证字段必须存在于给定的数据库表中。

Exists 规则基本用法

'state' => 'exists:states'

如果未指定column选项,则将使用字段名称。

指定自定义的表字段

'state' => 'exists:states,abbreviation'

有时,你可能需要指定要用于「exists」查询的特定数据库连接。你可以使用「点」语法将连接名称添加到表名称前来完成此操作:

'email' => 'exists:connection.staff,email'

如果您想自定义验证规则执行的查询,您可以使用Rule类来流畅地定义规则。在下面的例子中,我们还以数组的形式指定验证规则,而不是使用|字符来分隔它们:

use Illuminate\Validation\Rule; Validator::make($data, [ 'email' => [ 'required', Rule::exists('staff')->where(function ($query) { $query->where('account_id', 1); }), ], ]);

file

验证的字段必须是成功上传的文件。

filled

验证字段存在时不得为空。

gt:field

验证字段必须大于给定的field。两个字段必须是相同的类型。字符串、数字、数组和文件都使用size进行相同的评估。

gte:field

验证字段必须大于或等于给定的field。两个字段必须是相同的类型。字符串、数字、数组和文件都使用size进行相同的评估。

image

验证的文件必须是图片 (jpeg, png, bmp, gif, 或 svg)

in:foo,bar,...

验证字段必须包含在给定的值列表中。由于此规则通常要求您implode数组,因此可以使用Rule :: in方法流畅地构造规则:

use Illuminate\Validation\Rule; Validator::make($data, [ 'zones' => [ 'required', Rule::in(['first-zone', 'second-zone']), ], ]);

in_array:anotherfield

验证的字段必须存在于另一个字段anotherfield的值中。

integer

验证的字段必须是整数。

ip

验证的字段必须是 IP 地址。

ipv4

验证的字段必须是 IPv4 地址。

ipv6

验证的字段必须是 IPv6 地址。

json

验证的字段必须是有效的 JSON 字符串。

lt:field

验证中的字段必须小于给定的字段。这两个字段必须是相同的类型。字符串、数值、数组和文件大小的计算方式与size方法进行评估。

lte:field

验证中的字段必须小于或等于给定的字段。这两个字段必须是相同的类型。字符串、数值、数组和文件大小的计算方式与size方法进行评估。

max:value

验证中的字段必须小于或等于value。字符串、数字、数组或是文件大小的计算方式都用size方法进行评估。

mimetypes:text/plain,...

验证的文件必须与给定 MIME 类型之一匹配:

'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime'

要确定上传文件的 MIME 类型,会读取文件的内容来判断 MIME 类型,这可能与客户端提供的 MIME 类型不同。

mimes:foo,bar,...

验证的文件必须具有与列出的其中一个扩展名相对应的 MIME 类型。

MIME 规则基本用法

'photo' => 'mimes:jpeg,bmp,png'

即使你可能只需要验证指定扩展名,但此规则实际上会验证文件的 MIME 类型,其通过读取文件的内容以猜测它的 MIME 类型。

可以在以下链接中找到完整的 MIME 类型列表及其相应的扩展名:
https://svn.apache.org/repos/asf/httpd/htt...

min:value

验证中的字段必须具有最小值。字符串、数字、数组或是文件大小的计算方式都用size方法进行评估。

not_in:foo,bar,...

验证的字段不能包含在给定的值列表中。Rule::notIn方法可以用来构建规则:

use Illuminate\Validation\Rule; Validator::make($data, [ 'toppings' => [ 'required', Rule::notIn(['sprinkles', 'cherries']), ], ]);

not_regex:pattern

验证中的字段必须与给定的正则表达式不匹配。

在内部,此规则使用 PHPpreg_match函数。输入的值应遵循preg_match函数所需的相同格式,因此也包括有效的分隔符。例如:'email' => 'not_regex:/^.+$/i'

注意:当使用regex/not_regex模式时,可能需要在数组中指定规则,而不是使用管道分隔符,尤其是在正则表达式包含管道字符的情况下。

nullable

验证的字段可以为null。这在验证基本数据类型时特别有用,例如可以包含空值的字符串和整数。

numeric

验证的字段必须是数字。

present

验证的字段必须存在于输入数据中,但可以为空。

regex:pattern

验证的字段必须与给定的正则表达式匹配。

在内部,此规则使用 PHPpreg_match函数。输入的值应遵循preg_match函数所需的相同格式,因此也包括有效的分隔符。例如:'email' => 'regex:/^.+@.+$/i'

注意:当使用regex规则时,你必须使用数组,而不是使用|分隔符,特别是如果正则表达式包含|字符。

required

验证的字段必须存在于输入数据中,而不是空。如果满足以下条件之一,则字段被视为「空」:

  • 值为null
  • 值为空字符串。
  • 值为空数组或空Countable对象。
  • 值为无路径的上传文件。

required_if:anotherfield,value,...

如果anotherfield字段等于任一value,验证的字段必须出现且不为空 。

如果你想为required_if规则构造一个更复杂的条件,你可以使用Rule::requiredIf方法。此方法接受布尔值或闭包。当传递一个闭包时,应返回truefalse以确认是否需要验证字段:

use Illuminate\Validation\Rule; Validator::make($request->all(), [ 'role_id' => Rule::requiredIf($request->user()->is_admin),]); Validator::make($request->all(), [ 'role_id' => Rule::requiredIf(function () use ($request) { return $request->user()->is_admin; }), ]);

required_unless:anotherfield,value,...

如果anotherfield字段不等于任一value,验证的字段必须出现且不为空。

required_with:foo,bar,...

只有在其他任一指定字段出现时,验证的字段才必须出现且不为空。

required_with_all:foo,bar,...

只有在其他指定字段全部出现时,验证的字段才必须出现且不为空。

required_without:foo,bar,...

只在其他指定任一字段不出现时,验证的字段才必须出现且不为空。

required_without_all:foo,bar,...

只有在其他指定字段全部不出现时,验证的字段才必须出现且不为空。

same:field

验证的字段必须与给定字段匹配。

size:value

验证的字段必须具有与给定值匹配的大小。对于字符串,value 对应字符数。对于数字,value 对应给定的整数值。对于数组,size 对应数组的count值。对于文件,size 对应文件大小(单位 kb)。

starts_with:foo,bar,...

验证的字段必须以给定值之一开头。

string

验证的字段必须是一个字符串。如果允许这个字段为null,需要给这个字段分配nullable规则。

timezone

验证的字段必须是一个基于 PHP 函数timezone_identifiers_list的有效时区标识。

unique:table,column,except,idColumn

验证的字段在给定的数据库表中必须是唯一的。如果没有指定column,将会使用字段本身的名称。

指定自定义字段

'email' => 'unique:users,email_address'

自定义数据库连接

有时,你可能需要为验证程序创建的数据库查询设置自定义连接。上面的例子中,将unique:users设置为验证规则,等于使用默认数据库连接来查询数据库。如果要对其进行修改,请使用「点」方法指定连接和表名:

'email' => 'unique:connection.users,email_address'

强迫 Unique 规则忽略指定 ID :

有时,你可能希望在进行字段唯一性验证时忽略指定 ID 。例如, 在「更新个人资料」页面会包含用户名、邮箱和地点。这时你会想要验证更新的 E-mail 值是否唯一。如果用户仅更改了用户名字段而没有改 E-mail 字段,就不需要抛出验证错误,因为此用户已经是这个 E-mail 的拥有者了。

使用Rule类定义规则来指示验证器忽略用户的 ID。这个例子中通过数组来指定验证规则,而不是使用 | 字符来分隔:

use Illuminate\Validation\Rule; Validator::make($data, [ 'email' => [ 'required', Rule::unique('users')->ignore($user->id), ], ]);

{tip} 您永远不应将任何用户控制的请求输入传递给ignore方法。相反,您应该只从 Eloquent 模型实例传递系统生成的唯一 ID,例如自增 ID 或 UUID。否则,您的应用程序将容易受到 SQL 注入攻击。

你可以传递整个模型实例,而不是将模型键的值传递给ignore方法。Laravel 将自动从模型中提取主键:

Rule::unique('users')->ignore($user)

如果你的数据表使用的主键名称不是id,那就在调用ignore方法时指定字段的名称:

Rule::unique('users')->ignore($user->id, 'user_id')

默认情况下,unique规则将检查与要验证的属性的名称匹配的列是否唯一。但是你可以将不同的列名称作为第二个参数传递给unique方法:

Rule::unique('users', 'email_address')->ignore($user->id),

增加额外的 Where 语句:

你也可以通过where方法指定额外的查询条件。例如, 我们添加account_id1的约束:

'email' => Rule::unique('users')->where(function ($query) { return $query->where('account_id', 1); })

url

验证的字段必须是有效的 URL。

uuid

验证字段必须是有效的 RFC 4122(版本 1,3,4 或 5)通用唯一标识符(UUID)。

按条件增加规则

存在时则验证

在某些情况下,只有在该字段存在于数组中时, 才可以对字段执行验证检查。可通过增加sometimes到规则列表来实现:

$v = Validator::make($data, [ 'email' => 'sometimes|required|email',]);

在上面的例子中,email字段只有在$data数组中存在才会被验证。

{tip} 如果你尝试验证应该始终存在但可能为空的字段,请查阅可选字段的注意事项

复杂的条件验证

有时候你可能需要增加基于更复杂的条件逻辑的验证规则。例如,你可以希望某个指定字段在另一个字段的值超过 100 时才为必填。或者当某个指定字段存在时,另外两个字段才能具有给定的值。增加这样的验证条件并不难。首先,使用 静态规则 创建一个Validator实例:

$v = Validator::make($data, [ 'email' => 'required|email', 'games' => 'required|numeric', ]);

假设我们有一个专为游戏收藏家所设计的网页应用程序。如果游戏收藏家收藏超过一百款游戏,我们会希望他们来说明下为什么他们会拥有这么多游戏。比如说他们有可能经营了一家游戏分销商店,或者只是为了享受收集的乐趣。为了在特定条件下加入此验证需求,可以在Validator实例中使用sometimes方法。

$v->sometimes('reason', 'required|max:500', function ($input) { return $input->games >= 100; });

传入sometimes方法的第一个参数是要用来验证的字段名称。第二个参数是我们想使用的验证规则。闭包作为第三个参数传入,如果其返回true, 则额外的规则就会被加入。这个方法可以轻松地创建复杂的条件验证。你甚至可以一次对多个字段增加条件验证:

$v->sometimes(['reason', 'cost'], 'required', function ($input) { return $input->games >= 100; });

{tip} 传入闭包$input参数是Illuminate\Support\Fluent的一个实例,可用来访问你的输入或文件对象。

验证数组

验证表单的输入为数组的字段也不难。你可以使用 「点」方法来验证数组中的属性。例如,如果传入的 HTTP 请求中包含photos[profile]字段, 可以如下验证:

$validator = Validator::make($request->all(), [ 'photos.profile' => 'required|image', ]);

你也可以验证数组中的每个元素。例如,要验证指定数组输入字段中的每一个 email 是唯一的,可以这么做:

$validator = Validator::make($request->all(), [ 'person.*.email' => 'email|unique:users', 'person.*.first_name' => 'required_with:person.*.last_name', ]);

同理,你可以在语言文件定义验证信息时使用*字符,为基于数组的字段使用单个验证消息:

'custom' => [ 'person.*.email' => [ 'unique' => 'Each person must have a unique e-mail address', ] ],

自定义验证规则

使用规则对象

Laravel 提供了许多有用的验证规则;同时也支持自定义规则。注册自定义验证规则的方法之一,就是使用规则对象。可以使用 Artisan 命令make:rule来生成新的规则对象。接下来,让我们用这个命令生成一个验证字符串是大写的规则。Laravel 会将新的规则存放在app/Rules目录中:

php artisan make:rule Uppercase

一旦创建了规则,我们就可以定义它的行为。规则对象包含两个方法:passesmessagepasses方法接收属性值和名称,并根据属性值是否符合规则而返回truefalsemessage方法应返回验证失败时应使用的验证错误消息:


      

当然, 如果你希望从翻译文件中返回一个错误消息,你可以从message方法中调用辅助函数trans

/** * 获取验证错误消息。 * * @return string */ public function message(){ return trans('validation.uppercase'); }

一旦规则对象被定义好后,你可以通过将规则对象的实例和其他验证规则一起来传递给验证器:

use App\Rules\Uppercase;$request->validate([ 'name' => ['required', 'string', new Uppercase], ]);

使用闭包

如果你在应用程序中只需要一次自定义规则的功能,则可以使用闭包而不是规则对象。闭包接收属性的名称,属性的值如果验证失败则应该使用回调中的$fail

$validator = Validator::make($request->all(), [ 'title' => [ 'required', 'max:255', function ($attribute, $value, $fail) { if ($value === 'foo') { $fail($attribute.' is invalid.'); } }, ], ]);

使用扩展

注册自定义的验证规则的另一种方法是使用Validatorfacade 中的extend方法。让我们在 服务容器 中使用这个方法来注册自定义验证规则:


      

自定义的验证闭包接收四个参数:要被验证的属性名称$attribute、属性的值$value、传入验证规则的参数数组$parameters、以及Validator实列。

除了使用闭包,你也可以传入类和方法到extend方法中:

Validator::extend('foo', 'FooValidator@validate');

定义错误消息

你还需要为自定义规则定义错误信息。你可以使用内联自定义消息数组或者在验证语言文件中添加条目来实现这一功能。 消息应该被放到数组的第一位, 而不是在只用于存放属性指定错误信息的custom数组内:

"foo" => "Your input was invalid!", "accepted" => "The :attribute must be accepted.", // 其余的验证错误消息...

当创建一个自定义验证规则时,你可能有时候需要为错误信息定义自定义占位符。可以通过创建自定义验证器然后调用Validator门面上的replacer方法。你可以在 服务容器 的boot方法中执行如下操作:

/** * 启动应用程序。 * * @return void */ public function boot(){ Validator::extend(...); Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) { return str_replace(...); }); }

隐式扩展

默认情况下, 当所要验证的属性不存在或包含由required规则定义的空值时,那么正常的验证规则,包括自定义扩展将不会执行。 例如,unique规则将不会检验null值:

$rules = ['name' => 'unique']; $input = ['name' => null]; Validator::make($input, $rules)->passes(); // true

如果要求即使为空时也要验证属性,则必须要暗示属性是必须的。要创建这样一个「隐式」扩展, 可以使用Validator::extendImplicit()方法:

Validator::extendImplicit('foo', function ($attribute, $value, $parameters, $validator) { return $value == 'foo'; });

{note} 「隐式」扩展只暗示该属性是必需的。至于它到底是缺失的还是空值这取决于你。

本文章首发在 LearnKu.com网站上。