验证

简介

Laravel 提供了几种不同的方法验证传入应用的数据。默认情况下,Laravel 的控制器基类借助提供便利方法的 ValidatesRequests Trait,使用各种强大的验证规则验证传入的 HTTP 请求。

快速开始验证

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

定义路由

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

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

Route::post('post', 'PostController@store');

当然,GET 路由会为用户显示一个创建新的博客文章的表单,而 POST 路由将新的博客文章存储到数据库。

创建控制器

接下来,我们看一个简单的处理上述路由的控制器。我们暂时将 store 留空:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * 显示创建新博客文章的表单
     *
     * @return Response
     */
    public function create()
    {
        return view('post.create');
    }

    /**
     * 存储新的博客文章
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // 验证并存储博客文章
    }
}

编写验证逻辑

现在我们准备在 store 方法中编写逻辑来验证新创建的博客文章。为此,我们将使用 Illuminate\Http\Request 对象提供的 validate 方法。如果通过了验证规则,代码会继续正常运行;然而,如果验证失败,会抛出异常并自动将对应的错误响应发送给用户。对于传统的 HTTP 请求,会自动生成一个重定向响应,而对于 AJAX 请求会发送一个 JSON 响应。

为了更好地理解 validate 方法,我们回到 store 方法:

/**
 * 存储新的博客文章
 *
 * @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 路由中我们并没有明确地将错误信息绑定到视图。这是因为 Laravel 会自动检查 Session 数据中的错误信息,如果存在会自动将它们绑定到视图。$errors 变量是一个 Illuminate\Support\MessageBag 的实例。有关使用该对象的更多信息,请 查看其文档

$errors 变量是通过 Illuminate\View\Middleware\ShareErrorsFromSession 中间件绑定到视图的,该中间件由 web 中间件组提供。应用此中间件时,视图中始终可以使用 $errors 变量,从而可以方便地假设 $errors 变量始终已定义并可以安全使用。

因此,在我们的示例中,当验证失败时用户会被重定向到控制器的 create 方法,允许我们在此视图中显示错误信息:

/resources/views/post/create.blade.php

<h1>Create Post</h1>

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<!-- 创建文章的表单 -->

可选字段注意事项

默认情况下,Laravel 在应用的全局中间件栈中包含了 TrimStringsConvertEmptyStringsToNull 中间件。这些中间件在 App\Http\Kernel 类的中间件栈中列出。因此,如果您不希望验证器将 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 响应会使用 422 HTTP 状态码发送。

表单请求验证

创建表单请求

对于更复杂的验证场景,可以创建「表单请求」。表单请求是包含验证逻辑的自定义请求类。要创建一个表单请求类,可以使用 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',
    ];
}

可以在 rules 方法的参数中对任何所需依赖使用类型提示。它们会通过 服务容器 自动解析。

那么,如何运行验证规则呢?您需要做的就是在控制器操作中对请求使用类型提示。在调用控制器操作前,会验证传入的请求,这意味着不用将控制器和任何验证逻辑混在一起:

/**
 * 存储传入的博客文章
 *
 * @param  StoreBlogPost  $request
 * @return Response
 */
public function store(StoreBlogPost $request)
{
    // 传入的请求有效

    // 获取有效的输入数据
    $validated = $request->validated();
}

如果验证失败,会生成一个重定向响应将用户跳转回之前的位置。错误也会闪存到 Session 中,以便可用于显示。如果是一个 AJAX 请求,则会向用户返回具有 422 状态码的 HTTP 响应,里面包含 JSON 格式的验证错误信息。

添加表单请求后置钩子

如果要为表单请求添加一个「后置」钩子,可以使用 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;
}

可以在 authorize 方法的参数中对任何需要的依赖使用类型提示。它们会通过 Laravel 的 服务容器 自动解析。

自定义错误信息

可以通过重写 messages 方法来自定义表单请求使用的错误信息。该方法应该返回一个字段/规则对以及它们对应的错误信息的数组:

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

手动创建验证器

如果不想使用请求的 validate 方法,可以使用 Validator Facade 手动创建一个验证器实例。Facade 的 make 方法会生成一个新的验证器实例:

namespace App\Http\Controllers;

use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    /**
     * 存储新的博客文章
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'title' => 'required|unique:posts|max:255',
            'body' => 'required',
        ]);

        if ($validator->fails()) {
            return redirect('post/create')
                        ->withErrors($validator)
                        ->withInput();
        }

        // 存储博客文章
    }
}

传递给 make 方法的第一个参数是要验证的数据。第二个参数是要应用到数据的验证规则。

检查请求验证后,可以使用 withErrors 方法将错误信息闪存到 Session。使用该方法时,$errors 变量将在重定向之后自动共享给视图,使您可以轻松地将它们显示给用户。withErrors 方法接收验证器、MessageBag 或 PHP 数组。

自动重定向

如果想手动创建验证器实例,但仍然想利用请求的 validate 方法提供的自动重定向,可以在现有的验证器实例上调用 validate 方法。如果验证失败,用户将被自动重定向,或者在 AJAX 请求时,返回一个 JSON 响应:

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

命名错误包

如果单个页面中有多个表单,您可能希望命名错误的 MessageBag,来获取一个指定表单的错误信息。可以将名字作为第二个参数传递给 withErrors

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

然后可以从命名的 MessageBag 实例中获取 $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',
],

可用的验证规则

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

Accepted
Active URL
After (Date)
After Or Equal (Date)
Alpha
Alpha Dash
Alpha Numeric
Array
Bail
Before (Date)
Before Or Equal (Date)
Between
Boolean
Confirmed
Date
Date Equals
Date Format
Different
Digits
Digits Between
Dimensions (Image Files)
Distinct
E-Mail
Exists (Database)
File
Filled
Greater Than
Greater Than Or Equal
Image (File)
In
In Array
Integer
IP Address
JSON
Less Than
Less Than Or Equal
Max
MIME Types
MIME Type By File Extension
Min
Not In
Not Regex
Nullable
Numeric
Present
Regular Expression
Required
Required If
Required Unless
Required With
Required With All
Required Without
Required Without All
Same
Size
String
Timezone
Unique (Database)
URL
UUID

accepted

验证字段必须是 _yes_,_on_,_1_,或者 _true_。在验证接受「服务条款」时很有用。

active_url

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

after:_date_

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

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

除了传递一个日期字符串给 strtotime 进行验证,还可以指定另一个要与 _date_ 进行比较的字段:

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

after_or_equal:_date_

验证字段必须是给定日期 _date_ 之后或相等的值。有关更多信息,请参看 after 规则。

alpha

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

alpha_dash

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

alpha_num

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

array

验证字段必须是 PHP 数组。

bail

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

before:_date_

验证字段必须是给定日期 _date_ 之前的值。_date_ 将传递给 PHP 的 strtotime 函数。此外,和 after 规则一样,验证中的另一个字段的名称可以作为 date 值提供。

before_or_equal:_date_

验证字段必须是给定日期 _date_ 之前或相等的值。_date_ 将传递给 PHP 的 strtotime 函数。此外,和 after 规则一样,验证中的另一个字段的名称可以作为 date 值提供。

between:_min_,_max_

验证字段的大小必须介于给定的最小值 _min_ 和最大值 _max_ 之间。字符串,数字,数组和文件的验证方式与 size 规则相同。

boolean

验证字段必须能够转换为布尔值。接收输入的 truefalse10"1""0"

confirmed

验证字段必须有匹配的 foo_confirmation 字段。例如,如果验证字段是 password,则输入中必须存在匹配的 password_confirmation 字段。

date

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

date_equals:_date_

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

date_format:_format_

验证字段必须与给定的格式 _format_ 匹配。验证字段时应该使用 datedate_format 其中之一,而不是两个。

different:_field_

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

digits:_value_

验证字段必须是数字,并且必须有确切的长度 _value_。

digits_between:_min_,_max_

验证字段的长度必须在给定的最小值 _min_ 和最大值 _max_ 之间。

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

验证字段必须符合 e-mail 地址格式。

exists:_table_,_column_

验证字段必须存在于给定的数据表 _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_

验证字段必须小于给定字段 _field_。这两个字段必须是同一类型。字符串,数字,数组和文件会使用与 size 相同的规则。

lte:_field_

验证字段必须小于或等于给定字段 _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 类型及其对应扩展名的完整列表:https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types

min:_value_

验证字段必须大于或等于最小值 _value_。字符串,数字,数组和文件的验证方式与 size 规则相同。

not_in:_foo_,_bar_,...

验证字段不能包含在给定的值列表中。可使用 Rule::notIn 方法流畅地构造该规则:

use Illuminate\Validation\Rule;

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

not_regex:_pattern_

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

在内部,该规则使用了 PHP 的 preg_match 函数。指定的正则表达式应该和 preg_match 遵循相同的格式,因此也包括有效的分隔符。例如:'email' => 'regex:/^.+@.+$/i'

注意:当使用 regexnot_regex 模式时,必须要在数组中指定规则而不是使用 | 分隔,特别是当正则表达式中包含 | 符号时。

nullable

验证字段可能是 null。在验证基本数据类型如字符串和整数可以包含 null 值时非常有用。

numeric

验证字段必须是数字。

present

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

regex:_pattern_

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

在内部,该规则使用了 PHP 的 preg_match 函数。指定的正则表达式应该和 preg_match 遵循相同的格式,因此也包括有效的分隔符。例如:'email' => 'regex:/^.+@.+$/i'

注意:当使用 regexnot_regex 模式时,必须要在数组中指定规则而不是使用 | 分隔,特别是当正则表达式中包含 | 符号时。

required

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

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

required_if:_anotherfield_,_value_,...

如果另一个字段 _anotherfield_ 和任何值 _value_ 相等,那么该验证字段必须存在且不能为空。

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_ 对应于字符数。对于数字,_value_ 对应于给定的整数值。对于数组,_size_ 对应于数组的大小(count)。对于文件,_size_ 对应于文件大小(以 KB 为单位)。

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 字段,则不希望抛出一个验证错误,因为用户已经是 e-mail 地址的所有者。

要告诉验证器忽略给定用户的 ID,可以使用 Rule 来流畅地定义该规则。在本例中,我们会使用数组来指定验证规则,而不是用 | 符号来分隔规则:

use Illuminate\Validation\Rule;

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

如果数据表使用的主键列名不是 id,可以在调用 ignore 方法时指定列名:

'email' => Rule::unique('users')->ignore($user->id, '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,2,3,4 或 5)通用唯一标识符(UUID)。

按条件添加规则

存在才验证

有些情况下,可能希望 当字段存在于输入数组时才对字段运行验证检查。要快速完成此操作,将 sometimes 规则添加到规则列表:

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

在上述示例中,只有当 email 字段存在于 $data 数组中时才会验证。

如果要验证一个始终存在但可以为空的字段,请查看 可选字段注意事项

复杂条件验证

有时可能希望添加基于更复杂条件的逻辑的验证规则。例如,仅当另一个字段值大于 100 时需要给定字段。或者,仅当另一个字段存在时,需要两个字段有给定值。添加这些验证规则不一定非常痛苦。首先,使用不会改变的 _静态规则_ 创建一个 Validator 实例:

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

让我们假设我们的应用适用于游戏收藏家。如果有一个游戏收藏家注册了我们的应用并拥有超过 100 款游戏,我们希望他解释为什么拥有这么多游戏。例如,可能他经营了一家游戏转售店,或者他只是喜欢收藏。要有条件的添加此要求,可以在 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;
});

传递给闭包的 $input 参数将是一个 Illuminate\Support\Fluent 的实例,可用于访问输入和文件。

验证数组

验证基于数组的表单输入字段不一定非常痛苦。可以使用「点」表示法来验证数组中的属性。例如,如果传入的 HTTP 请求包含一个 photos[profile] 字段,可以像这样验证它:

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

还可以验证数组的每个元素。例如,要验证给定数组输入字段中的每个 e-mail 是否唯一,可以执行以下操作:

$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 方法应返回验证失败时使用的验证错误信息:

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Uppercase implements Rule
{
    /**
     * 判断是否通过验证规则
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return strtoupper($value) === $value;
    }

    /**
     * 获取验证错误信息
     *
     * @return string
     */
    public function message()
    {
        return 'The :attribute must be uppercase.';
    }
}

当然,如果想从翻译文件中返回错误信息,可以在 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.');
            }
        },
    ],
]);

使用扩展

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

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 启动任何应用服务
     *
     * @return void
     */
    public function boot()
    {
        Validator::extend('foo', function ($attribute, $value, $parameters, $validator) {
            return $value == 'foo';
        });
    }

    /**
     * 注册服务提供者
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

自定义验证器闭包接收四个参数:要验证的属性名 $attribute,属性值 $value,传递给规则的数组参数 $parameters 以及 Validator 实例。

还可以给 extend 方法传递一个类和方法而不是一个闭包:

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

定义错误信息

还需要为自定义规则定义错误信息。可以使用内联的自定义信息数组或在验证语言文件中添加条目来执行此操作。该信息应放在数组的第一级,而不是在 custom 数组内,这仅适用于指定属性的错误信息:

"foo" => "Your input was invalid!",

"accepted" => "The :attribute must be accepted.",

// 其它验证错误信息

创建自定义验证规则时,有可能需要为错误信息定义自定义替换占位符。可以通过创建上述自定义验证器,然后调用 Validator Facade 的 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';
});

「隐式」扩展仅 _表明_ 该属性是必填的。属性实际上是否可以不存在或为空取决于您。