表单验证

未匹配的标注

表单验证

简介

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

快速验证

为了了解 Laravel 强大的验证功能,我们先看一个表单验证并将错误展示给用户的完整示例。

定义路由

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

use App\Http\Controllers\PostController;

Route::get('post/create', [PostController::class, 'create']);

Route::post('post', [PostController::class, 'store']);

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

创建控制器

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

<?php

namespace App\Http\Controllers;

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

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 方法中。另外再提醒一次,如果验证失败,会自动生成一个对应的响应。如果验证通过,那我们的控制器会继续正常运行。

或者,验证规则可以使用数组而不是单个 | 分隔的字符串:

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

你可以使用 validateWithBag 方法来验证请求,并将所有错误信息储存在一个 命名错误信息包 中:

$validatedData = $request->validateWithBag('post', [
    'title' => ['required', 'unique:posts', 'max:255'],
    'body' => ['required'],
]);

在首次验证失败时停止运行

有时候我们希望某个字段在第一次验证失败后就停止运行验证规则,只需要将 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',
]);

另外,如果你的字段名称包含点,则可以通过使用反斜杠将点转义,以防止将其解释为“点”语法:

$request->validate([
    'title' => 'required|unique:posts|max:255',
    'v1\.0' => 'required',
]);

显示验证错误信息

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

再次提示,我们没有必要在 GET 路由中显式地绑定错误信息到视图中。这是因为 Laravel 会检查 session 数据中的错误,如果可用的话,将会自动将其绑定到视图中去。其中的 $errors 变量是 Illuminate\Support\MessageBag 的一个实例。要获取关于该对象更多的信息, 请参阅这篇文档

技巧:$errorsweb 中间件组提供的 Illuminate\View\Middleware\ShareErrorsFromSession 中间件绑定到视图中。当该中间件被应用后,$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

<!-- Create Post Form -->

@error 指令

您亦可使用 @error Blade 指令方便地检查给定的属性是否存在验证错误信息。在 @error 指令中,您可以输出 $message 变量以显示错误信息:

<!-- /resources/views/post/create.blade.php -->

<label for="title">Post Title</label>

<input id="title" type="text" class="@error('title') is-invalid @enderror">

@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

关于可选字段的注意事项

默认情况下, 在您的 Laravel 应用的全局中间件堆栈 App\Http\Kernel 类中包含了 TrimStringsConvertEmptyStringsToNull 中间件。因此,如果您不想让 null 被验证器标识为非法的话,您需要将「可选」字段标志为 nullable

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

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

AJAX 请求和验证

在上述例子中,我们使用传统的表单发送数据到应用。当然,许多应用可能使用 AJAX 来进行请求,此时, Laravel 不会生成重定向响应。取而代之的, Laravel 将会生成一个包含错误信息的 JSON 响应,该响应的 HTTP 状态码将被置为 422 。

验证表单请求

创建表单请求验证

面对更复杂的情况,您可以创建一个「表单请求」来应对更复杂的验证逻辑。表单请求是一个包含了验证逻辑的自定义请求类。要创建一个表单请求类,请使用 make:request Artisan CLI 命令:

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 方法传入所需的任何依赖项。它们将被 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',
    ];
}

由于所有的表单请求都继承自 Laravel 的请求基类,因此,我们可以使用 user 方法以获取当前通过验证的用户。同时请注意上述例子中对 route 方法的调用。该方法允许您在被访问的路由上访问其定义的 URI 参数,譬如下方例子中的 {comment} 的参数:

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

如果 authorize 方法返回 false,应用将会自动返回 403 HTTP 响应,且您的控制器方法将不会被执行。

如果您想要在您的应用的其他部分处理授权逻辑,请从 authorize 方法返回 true

/**
 * 判断该请求中的用户是否拥有授权
 *
 * @return bool
 */
public function authorize()
{
    return true;
}

技巧:您可以向 authorize 方法传入所需的任何依赖。它将会被 Laravel 服务容器 自动解析。

自定义错误信息

您可以通过重写表单请求的 message 方法以自定义错误信息。该方法应返回属性/规则对及其对应的错误信息的数据:

/**
 * 获取已定义的验证规则的错误信息
 *
 * @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',
    ];
}

准备验证输入

如果您需要在应用验证规则前清除请求中的任何数据,您可以使用 prepareForValidation 方法:

use Illuminate\Support\Str;

/**
 * 准备验证数据
 *
 * @return void
 */
protected function prepareForValidation()
{
    $this->merge([
        'slug' => Str::slug($this->slug),
    ]);
}

手动创建验证器

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

<?php

namespace App\Http\Controllers;

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

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();
        }

        // Store the blog post...
    }
}

make 方法中的第一个参数是期望校验的数据。第二个参数是应用到数据上的校验规则。

如果校验失败,您可以使用 withErrors 方法将错误信息闪存至 session 中。使用该方法时, $errors 会自动与之后的视图共享,您可以很方便将其回显给用户。withErrors 方法接受验证器实例, MessageBag 或是 PHP 数组

自动重定向

如果您想要在手动创建验证器实例的同时,使用请求的 validate 方法的自动重定向功能,那么您可以在现有的验证器实例上调用 validate 方法。如果校验失败,用户将被自动重定向,或在 AJAX 请求下,将会返回一个 JSON 响应:

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

如果校验失败,您可以使用 validateWithBag 方法将错误信息存储到 命名错误包 中。

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

命名错误包

如果您的一个页面中存在多个表单,您可以为错误的 MessageBag 命名,用于检索指定表单的错误信息。只需将名称作为 withErrors 的第二个参数传递给它:

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

您可以通过 $errors 变量访问命名后的 MessageBag 实例:

{{ $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 语言文件中的 attributes 数组指定自定义信息来实现:

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

指定自定义属性值

如果您想要将错误信息中的 :attribute 部分替换为自定义的属性名称,您可以在 resources/lang/xx/validation.php 语言文件中的 attributes 属性数组中指定自定义名称以实现:

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

您亦可通过将自定义属性作为 Validator::make 的第四个参数传递给它来实现:

$customAttributes = [
    'email' => 'email address',
];

$validator = Validator::make($input, $rules, $messages, $customAttributes);

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

有时,您可能需要将错误信息中的 :value 部分替换为自定义的表示形式。例如,下方规则中将 cc 指定为 payment_type 的值:

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

如果规则校验失败,它将生成如下的错误信息:

The credit card number field is required when payment type is cc.

要将 payment type 显示的 cc 替换为自定义的显示形式,您可以通过在 validation 语言文件中定义 values 数组来实现之:

'values' => [
    'payment_type' => [
        'cc' => 'credit card'
    ],
],

现在,如果规则校验失败,将生成如下的错误信息:

The credit card number field is required when payment type is credit card.

可用的验证规则

下方列出了所有可用的验证规则及其功能:

accepted

待验证字段必须是 yeson1true 。这在确认「服务条款」是否同意时很有用。

active_url

根据 PHP 函数 dns_get_record ,验证字段必须具有有效的 A 或 AAAA 记录。URL 所提供的主机名时在传递给 dns_get_record 函数前使用 parse_url 函数获取到的。

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。此外,与 after 规则一致,可以将另外一个待验证的字段作为 date 的值。

before_or_equal:date

验证字段必须是在给定日期之前或与之相同的日期。这个日期值将会被传递给 PHP 的 strtotime 函数来计算。除此之外,像 after 规则一样,验证中另一个字段的名称可以作为值传递给 date

between:min,max

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

boolean

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

confirmed

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

date

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

date_equals:date

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

date_format:format

验证字段必须匹配给定的日期格式。当验证某个字段的时候,你应该 只使用 date 或者 date_format ,而不是同时使用。此验证规则支持 PHP 所有的 DateTime 类。

different:field

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

digits:value

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

digits_between:min,max

验证中的字段必须为 numeric,并且长度必须在给定的 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 这样的 float 来指定::

'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 地址格式。当前版本,此种验证规则由 egulias/email-validator 提供支持。默认使用 RFCValidation 验证样式,但你也可以使其他验证样式:

'email' => 'email:rfc,dns'

例子使用 RFCValidationDNSCheckValidation 验证样式。所有可用验证样式列表:

  • rfc: RFCValidation
  • strict: NoRFCWarningsValidation
  • dns: DNSCheckValidation
  • spoof: SpoofCheckValidation
  • filter: FilterEmailValidation

当下版本 filter 验证规则使用 PHP 的 filter_var 方法进行验证,在 5.8 版本接入 Laravel 。 dnsspoof 验证器需要 PHP 的 intl 扩展。

ends_with:foo,bar,…

验证的字段必须以给定的值之一结尾。

exclude_if:anotherfield,value

如果_anotherfield_字段等于_value_,验证下的字段将被 validatevalidated 方法返回的请求数据排除。

exclude_unless:anotherfield,value

验证下的字段将被 validatevalidated 方法返回的请求数据排除,除非 anotherfield 的字段等于 value

exists:table,column

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

Exists 规则的基本用法

'state' => 'exists:states'

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

指定列名

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

如果你需要指定 exists 方法用来查询的数据库。你可以通过使用「点」语法将数据库的名称添加到数据表前面来实现这个目的:

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

你也可以使用关联模型来指定表名。

'user_id' => 'exists:App\Models\User,id'

如果要自定义验证规则执行的查询,可以使用 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, or webp)

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

验证的字段必须是整数。

注意:此种验证规则不是验证数据是 「integer」 类型,仅验证字符串或数值包含一个「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 类型列表及相对应的扩展名: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

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

验证时,这个规则使用 PHP preg_match 函数。指定的模式应遵循 preg_match 所需的相同格式,也包括有效的分隔符。 例如: 'email' => 'not_regex:/^.+$/i'

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

nullable

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

numeric

验证字段必须为数值。

password

验证字段必须与当前登录用户的密码相同。你可以通过传入第一个参数来指定身份验证看守器(Authentication Guard)。

'password' => 'password:api'

present

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

regex:pattern

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

验证时,这个规则使用 PHP 的 preg_match 函数。 指定的模式应遵循 preg_match 所需的相同格式,也包括有效的分隔符。 例如: 'email' => 'not_regex:/^.+$/i'

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

required

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

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

required_if:anotherfield,value,…

如果其它字段 _anotherfield_ 为任一值( _value1__value2__value3_ 等,也可只有一个 _value1_) ,则此验证字段必须存在且不为空。

如果您需要构造更复杂的条件 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 对应给定的整数值(attribute 必须有 numeric 或者 integer 规则)。对于数组,size 对应数组的 count 值。对于文件,size 对应文件大小(单位 kB)。让我们来看几个例子:

// 验证字符串长度是否为 12...
'title' => 'size:12';

// 验证数字是否为 10...
'seats' => 'integer|size:10';

// 验证数组的长度(拥有的元素)是否为 5...
'tags' => 'array|size:5';

// 验证上传的文件是否为 512 kB...
'image' => 'file|size:512';

starts_with:foo,bar,…

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

string

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

timezone

验证字段必须为符合 PHP 函数 timezone_identifiers_list 所定义的有效时区标识。

unique:table,column,except,idColumn

验证字段在给定的数据库表中必须是唯一的。

指定自定义数据表 / 列名:

除直接指定表名外,你也可以指定 Eloquent 模型。

'email' => 'unique:App\Models\User,email_address'

column 选项可用于指定相应数据库列的字段。 如果未指定 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),
    ],
]);

注意:您永远不应该将任何用户控制的请求输入传递给 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)。

按条件增加规则

字段包含特定值跳过验证

如果另一个字段具有给定的值,您可能偶尔希望不验证给定字段。您可以使用 exclude_if 验证规则来实现这一点。在本例中,如果 has_appointment 字段的值为 false ,则不会验证 appointment_datedoctor_name 字段:

$v = Validator::make($data, [
    'has_appointment' => 'required|bool',
    'appointment_date' => 'exclude_if:has_appointment,false|required|date',
    'doctor_name' => 'exclude_if:has_appointment,false|required|string',
]);

您可以使用 exclude_unless 规则来验证给定字段,除非另一个字段具有给定的值:

$v = Validator::make($data, [
    'has_appointment' => 'required|bool',
    'appointment_date' => 'exclude_unless:has_appointment,true|required|date',
    'doctor_name' => 'exclude_unless:has_appointment,true|required|string',
]);

存在时则验证

在某些情况下,你可能希望将要验证的字段存在于输入数组中时,才对该字段执行验证。可以在规则列表中增加 sometimes 来实现:

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

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

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

复杂条件验证

有时候你可能需要增加基于更复杂的条件逻辑的验证规则。例如,你可以希望某个指定字段在另一个字段的值超过 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;
});

技巧:传递给 闭包$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 提供了多种多样有用的校验规则;但您亦可进行自定义。注册自定义校验规则的方法之一便是使用规则对象。您可以使用 make:rule 生成新的规则对象。接下来,让我们使用该命令生成一个校验字符串是否是大写的规则, Laravel 会将新规则置于 app/Rules 目录中:

php artisan make:rule Uppercase

当规则创建成功后,我们便可定义其行为。规则对象包含两个方法: passesmessagepasses 方法接收属性值及其名称,它应该返回以 truefalse 表示的属性值是否通过验证的结果。 message 方法应该返回验证失败时使用的错误信息:

<?php

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 门面 中使用 extend 方法。接下来,让我们在 服务提供者 中使用这个方法来注册一个自定义校验规则:

<?php

namespace App\Providers;

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

class AppServiceProvider extends ServiceProvider
{
    /**
     * 注册服务提供器
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * 驱动应用服务
     *
     * @return void
     */
    public function boot()
    {
        Validator::extend('foo', function ($attribute, $value, $parameters, $validator) {
            return $value == 'foo';
        });
    }
}

自定义校验闭包函数接收四个参数:要被验证的属性名称 $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(...);
    });
}

隐式扩展

默认情况下,当要验证的属性不存在或者包含一个空字符串时,包含自定义扩展的校验规则是不会执行的。例如, unique 规则不会校验空字符串:

$rules = ['name' => 'unique:users,name'];

$input = ['name' => ''];

Validator::make($input, $rules)->passes(); // true

如果想要实现即使属性为空,规则也要校验,那么就需要暗示该属性是必须的。要创建这样的 「隐式」扩展,请使用 Validator::extendImplicit() 方法:

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

注意:「隐式」扩展仅仅暗示该属性是必须的。至于它到底是确实还是空值,这取决于你。

隐式规则对象

如果您想在属性为空时执行规则对象,那么您应该实现 Illuminate\Contracts\Validation\ImplicitRule 接口。这个接口将充当验证器的 「标识接口」;因此,它不会包含任何您需要实现的方法。

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

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
上一篇 下一篇
Summer
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
贡献者:9
讨论数量: 0
发起讨论 只看当前版本


暂无话题~