验证

介绍

Laravel 提供了多种不同的方法来验证应用程序的传入数据。最常见的方法是使用所有传入 HTTP 请求上可用的 validate 方法。然而,我们也会讨论其它验证方法。

Laravel 包含了许多方便的验证规则,您可以将这些规则应用于数据,甚至提供了验证值是否在给定的数据库表中唯一的功能。我们将详细讲解每个验证规则,以便您熟悉 Laravel 的所有验证功能。

验证快速入门

要了解 Laravel 强大的验证功能,让我们看一个完整的示例,演示如何验证表单并将错误消息显示给用户。通过阅读这个概述,您将能够对如何使用 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 Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PostController extends Controller
{
    /**
     * 显示创建新博客帖子的表单。
     */
    public function create(): View
    {
        return view('post.create');
    }

    /**
     * 存储新的博客帖子。
     */
    public function store(Request $request): RedirectResponse
    {
        // 验证并存储博客帖子...

        $post = /** ... */

        return to_route('post.show', ['post' => $post->id]);
    }
}

编写验证逻辑

现在我们准备好在 store 方法中填写逻辑来验证新的博客帖子。为此,我们将使用 Illuminate\Http\Request 对象提供的 validate 方法。如果验证规则通过,代码将正常执行;但如果验证失败,Illuminate\Validation\ValidationException 异常将被抛出,并且正确的错误响应将自动返回给用户。

如果在传统的 HTTP 请求中验证失败,将会生成一个重定向响应到之前的 URL。如果传入请求是 XHR 请求,将返回【包含验证错误信息的 JSON 响应】。

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

/**
 * 存储新的博客帖子。
 */
public function store(Request $request): RedirectResponse
{
    $validated = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // 博客帖子是有效的...

    return redirect('/posts');
}

如你所见,验证规则是作为数组传递给 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 会自动将用户重定向回之前的位置。此外,所有的验证错误和【请求输入】将 【自动闪存到会话】中。

$errors 变量由 Illuminate\View\Middleware\ShareErrorsFromSession 中间件共享给应用的所有视图,而这个中间件是由 web 中间件组提供的。当这个中间件应用时,$errors 变量将在所有视图中始终可用,这使得你可以方便地假设 $errors 变量始终已定义且可以安全使用。$errors 变量将是 Illuminate\Support\MessageBag 的一个实例。有关如何使用这个对象的更多信息,请【查阅它的文档】。

所以,在我们的例子中,当验证失败时,用户将被重定向到控制器的 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 -->

自定义错误信息

Laravel 内置的验证规则每个都有一个错误信息,这些错误信息位于你应用的 lang/en/validation.php 文件中。如果你的应用没有 lang 目录,你可以通过运行 lang:publish Artisan 命令来让 Laravel 创建它。

lang/en/validation.php 文件中,你将找到每个验证规则的翻译条目。你可以根据你应用的需求自由更改或修改这些消息。

此外,你还可以将这个文件复制到其它语言目录,以便为你应用的语言翻译消息。要了解更多关于 Laravel 本地化的信息,请查看完整的【本地化文档】。

默认情况下,Laravel 应用骨架不包括 lang 目录。如果你想自定义 Laravel 的语言文件,可以通过 lang:publish Artisan 命令发布它们。

XHR 请求和验证

在这个例子中,我们使用了传统的表单来向应用发送数据。然而,许多应用会接收来自 JavaScript 前端的 XHR 请求。当在 XHR 请求中使用 validate 方法时,Laravel 不会生成重定向响应。相反,Laravel 会生成一个【包含所有验证错误的 JSON 响应】。此 JSON 响应将附带 422 HTTP 状态码。

@error 指令

你可以使用 @error 【Blade】 指令快速确定给定属性是否有验证错误信息。在 @error 指令内,你可以回显 $message 变量来显示错误信息:

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

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

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

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

如果你使用的是【命名错误包】,你可以将错误包的名称作为第二个参数传递给 @error 指令:

<input ... class="@error('title', 'post') is-invalid @enderror">

重新填充表单

当 Laravel 因验证错误生成重定向响应时,框架会自动【将请求的所有输入闪存到会话中】。这样做是为了方便你在下一个请求中访问这些输入,并重新填充用户尝试提交的表单。

要从上一个请求中检索闪存的输入,可以在 Illuminate\Http\Request 的实例上调用 old 方法。old 方法将从【会话】中提取先前闪存的输入数据:

$title = $request->old('title');

Laravel 还提供了一个全局的 old 助手。如果你在 【Blade 模板】中显示旧输入,使用 old 助手来重新填充表单会更加方便。如果给定字段没有旧输入,则会返回 null

<input type="text" name="title" value="{{ old('title') }}">

关于可选字段的说明

默认情况下,Laravel 在应用程序的全局中间件堆栈中包含了 TrimStringsConvertEmptyStringsToNull 中间件。因此,如果你不希望验证器将 null 值视为无效,你通常需要将 “可选” 的请求字段标记为 nullable。例如:

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

在这个例子中,我们指定 publish_at 字段可以是 null 或有效的日期表示。如果没有在规则定义中添加 nullable 修饰符,验证器会将 null 视为无效的日期。

验证错误响应格式

当你的应用抛出一个 Illuminate\Validation\ValidationException 异常,并且传入的 HTTP 请求期待一个 JSON 响应时,Laravel 会自动格式化错误信息,并返回一个 422 Unprocessable Entity HTTP 响应。

下面是一个验证错误的 JSON 响应格式示例。请注意,嵌套的错误键会被展平为 “点” 符号表示法:

{
    "message": "The team name must be a string. (and 4 more errors)",
    "errors": {
        "team_name": [
            "The team name must be a string.",
            "The team name must be at least 1 characters."
        ],
        "authorization.role": [
            "The selected authorization.role is invalid."
        ],
        "users.0.email": [
            "The users.0.email field is required."
        ],
        "users.2.email": [
            "The users.2.email must be a valid email address."
        ]
    }
}

表单请求验证

创建表单请求

对于更复杂的验证场景,你可能希望创建一个 “表单请求”。表单请求是自定义请求类,它封装了自己的验证和授权逻辑。要创建一个表单请求类,可以使用 make:request Artisan CLI 命令:

php artisan make:request StorePostRequest

生成的表单请求类将被放置在 app/Http/Requests 目录下。如果此目录不存在,执行 make:request 命令时会自动创建。每个由 Laravel 生成的表单请求都有两个方法:authorizerules

正如你所猜测的,authorize 方法负责确定当前已认证的用户是否可以执行请求所代表的操作,而 rules 方法则返回应该应用于请求数据的验证规则。

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

你可以在 rules 方法的签名中为任何需要的依赖项添加类型提示。它们将通过 Laravel 的【服务容器】自动解析。

如何评估验证规则?

只需要在控制器方法中对请求进行类型提示。表单请求在控制器方法调用之前会自动进行验证,这意味着你无需在控制器中添加任何验证逻辑:

/**
 * 存储新的博客文章。
 */
public function store(StorePostRequest $request): RedirectResponse
{
    // 请求是有效的...

    // 获取经过验证的输入数据...
    $validated = $request->validated();

    // 获取经过验证的部分数据...
    $validated = $request->safe()->only(['name', 'email']);
    $validated = $request->safe()->except(['name', 'email']);

    // 存储博客文章...

    return redirect('/posts');
}

如果验证失败,将生成一个重定向响应,用户将被发送回之前的位置。错误信息也会被闪存到会话中,以便在视图中显示。如果请求是一个 XHR 请求,Laravel 会返回一个 422 状态码的 HTTP 响应,其中包含【验证错误的 JSON 表示】。

需要为你的 Inertia 驱动的 Laravel 前端添加实时表单请求验证吗?可以查看 Laravel Precognition

执行额外的验证

有时,你需要在初始验证完成后执行额外的验证。可以通过使用表单请求的 after 方法来实现。

after 方法应返回一个包含可调用函数或闭包的数组,这些闭包会在验证完成后执行。给定的可调用函数将接收一个 Illuminate\Validation\Validator 实例,允许你在必要时抛出额外的错误信息:

use Illuminate\Validation\Validator;

/**
 * 获取请求的 "after" 验证可调用函数。
 */
public function after(): array
{
    return [
        function (Validator $validator) {
            if ($this->somethingElseIsInvalid()) {
                $validator->errors()->add(
                    'field',
                    '该字段存在问题!'
                );
            }
        }
    ];
}

如上所示,after 方法返回的数组也可以包含可调用的类。类的 __invoke 方法将接收一个 Illuminate\Validation\Validator 实例:

use App\Validation\ValidateShippingTime;
use App\Validation\ValidateUserStatus;
use Illuminate\Validation\Validator;

/**
 * 获取请求的 "after" 验证可调用函数。
 */
public function after(): array
{
    return [
        new ValidateUserStatus,
        new ValidateShippingTime,
        function (Validator $validator) {
            //
        }
    ];
}

在第一次验证失败时停止

通过在请求类中添加 stopOnFirstFailure 属性,你可以告诉验证器,一旦某个验证规则失败,就停止验证所有属性:

/**
 * 指示验证器是否应该在第一次规则失败时停止。
 *
 * @var bool
 */
protected $stopOnFirstFailure = true;

自定义重定向位置

当表单请求验证失败时,Laravel 会生成一个重定向响应,将用户发送回他们之前的位置。然而,你可以自由地自定义这种行为。为此,只需在表单请求中定义 $redirect 属性:

/**
 * 如果验证失败,用户应被重定向到的 URI。
 *
 * @var string
 */
protected $redirect = '/dashboard';

或者,如果你希望用户被重定向到一个命名路由,可以定义 $redirectRoute 属性:

/**
 * 如果验证失败,用户应被重定向到的路由。
 *
 * @var string
 */
protected $redirectRoute = 'dashboard';

授权表单请求

表单请求类还包含一个 authorize 方法。在此方法中,你可以判断认证用户是否有权更新某个资源。例如,你可以判断用户是否拥有他们试图更新的博客评论。通常,你将在此方法中与【授权门(gates)和策略(policies)】进行交互:

use App\Models\Comment;

/**
 * 判断用户是否有权进行此请求。
 */
public function authorize(): bool
{
    $comment = Comment::find($this->route('comment'));

    return $comment && $this->user()->can('update', $comment);
}

由于所有表单请求都继承自基础的 Laravel 请求类,你可以使用 user 方法来访问当前认证的用户。还要注意上面示例中对 route 方法的调用。该方法让你可以访问正在调用的路由中定义的 URI 参数,比如下面的 {comment} 参数:

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

因此,如果你的应用程序利用了路由模型绑定(【route model binding】),你的代码可以通过访问请求中的已解析模型属性,使代码更加简洁:

return $this->user()->can('update', $this->comment);

如果 authorize 方法返回 false,则会自动返回一个 403 状态码的 HTTP 响应,并且你的控制器方法将不会执行。

如果你打算在应用程序的其它部分处理请求的授权逻辑,你可以完全移除 authorize 方法,或者简单地返回 true

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

你可以在 authorize 方法的签名中类型提示任何需要的依赖项。它们将通过 Laravel 的【服务容器】自动解析。

自定义错误信息

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

/**
 * 获取定义的验证规则的错误消息。
 *
 * @return array<string, string>
 */
public function messages(): array
{
    return [
        'title.required' => '标题是必需的',
        'body.required' => '消息内容是必需的',
    ];
}

自定义验证属性

Laravel 的许多内置验证规则错误消息包含 :attribute 占位符。如果你希望将验证消息中的 :attribute 占位符替换为自定义的属性名称,你可以通过重写 attributes 方法来指定自定义名称。此方法应返回一个属性/名称对的数组:

/**
 * 获取验证错误的自定义属性。
 *
 * @return array<string, string>
 */
public function attributes(): array
{
    return [
        'email' => '电子邮件地址',
    ];
}

准备验证输入

如果你需要在应用验证规则之前准备或清理请求中的数据,你可以使用 prepareForValidation 方法:

use Illuminate\Support\Str;

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

同样,如果你需要在验证完成后规范化任何请求数据,你可以使用 passedValidation 方法:

/**
 * 处理通过验证后的操作。
 */
protected function passedValidation(): void
{
    $this->replace(['name' => 'Taylor']);
}

prepareForValidation 方法允许你在验证之前修改请求数据,而 passedValidation 方法则允许你在验证通过后修改数据。

手动创建验证器

如果你不想在请求上使用 validate 方法,你可以通过 Validator 【facade】 手动创建一个验证器实例。make 方法用于生成一个新的验证器实例:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

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

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

        // 获取经过验证的输入...
        $validated = $validator->validated();

        // 获取经过验证的输入的部分数据...
        $validated = $validator->safe()->only(['name', 'email']);
        $validated = $validator->safe()->except(['name', 'email']);

        // 存储博客文章...

        return redirect('/posts');
    }
}

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

在确定请求验证失败后,你可以使用 withErrors 方法将错误信息闪存到会话中。当使用这个方法时,$errors 变量会自动与视图共享,便于你在重定向后轻松地将错误信息显示给用户。withErrors 方法接受一个验证器、MessageBag 或 PHP 数组。

停止在第一次验证失败时

stopOnFirstFailure 方法会通知验证器,在第一次验证失败时停止验证所有属性:

if ($validator->stopOnFirstFailure()->fails()) {
    // ...
}

自动重定向

如果你希望手动创建一个验证器实例,但仍然利用 HTTP 请求的 validate 方法提供的自动重定向功能,你可以在现有的验证器实例上调用 validate 方法。如果验证失败,用户将自动被重定向,或者在 XHR 请求的情况下,【返回一个 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') }}

自定义错误信息

如果需要,您可以为验证器实例提供自定义的错误信息,以替代 Laravel 默认的错误信息。有几种方法可以指定自定义消息。首先,您可以将自定义消息作为第三个参数传递给 Validator::make 方法:

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

在此示例中,: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 email address!',
];

指定自定义属性值

Laravel 的许多内置错误消息包含一个 :attribute 占位符,它会被验证字段或属性的名称替换。为了自定义特定字段的占位符替换值,您可以将自定义属性的数组作为第四个参数传递给 Validator::make 方法:

$validator = Validator::make($input, $rules, $messages, [
    'email' => 'email address',
]);

执行额外的验证

有时,您可能需要在初始验证完成后执行额外的验证。您可以使用验证器的 after 方法来实现这一点。after 方法接受一个闭包或一个可调用数组,这些可调用方法将在验证完成后执行。给定的可调用方法将接收一个 Illuminate\Validation\Validator 实例,允许您在必要时添加额外的错误消息:

use Illuminate\Support\Facades\Validator;

$validator = Validator::make(/* ... */);

$validator->after(function ($validator) {
    if ($this->somethingElseIsInvalid()) {
        $validator->errors()->add(
            'field', 'Something is wrong with this field!'
        );
    }
});

if ($validator->fails()) {
    // ...
}

如上所述,after 方法也接受一个可调用数组,这对于将 “验证后” 逻辑封装在可调用类中非常方便。每个类的 __invoke 方法将接收一个 Illuminate\Validation\Validator 实例:

use App\Validation\ValidateShippingTime;
use App\Validation\ValidateUserStatus;

$validator->after([
    new ValidateUserStatus,
    new ValidateShippingTime,
    function ($validator) {
        // ...
    },
]);

使用验证后的输入

在使用表单请求或手动创建的验证器实例验证传入的请求数据后,您可能希望检索实际经过验证的请求数据。可以通过几种方式实现这一点。首先,您可以在表单请求或验证器实例上调用 validated 方法。此方法返回一个经过验证的数据数组:

$validated = $request->validated();

$validated = $validator->validated();

或者,您可以在表单请求或验证器实例上调用 safe 方法。此方法返回一个 Illuminate\Support\ValidatedInput 实例。该对象提供 onlyexceptall 方法,用于检索验证数据的子集或整个经过验证的数据数组:

$validated = $request->safe()->only(['name', 'email']);

$validated = $request->safe()->except(['name', 'email']);

$validated = $request->safe()->all();

此外,Illuminate\Support\ValidatedInput 实例可以像数组一样进行迭代和访问:

// 可以迭代经过验证的数据...
foreach ($request->safe() as $key => $value) {
    // ...
}

// 可以像访问数组一样访问经过验证的数据...
$validated = $request->safe();

$email = $validated['email'];

如果您想将额外的字段添加到经过验证的数据中,可以调用 merge 方法:

$validated = $request->safe()->merge(['name' => 'Taylor Otwell']);

如果您想将验证后的数据作为【集合】实例检索,可以调用 collect 方法:

$collection = $request->safe()->collect();

使用错误信息

在调用 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')) {
    // ...
}

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

Laravel 内置的验证规则每个都有一个错误消息,这些消息位于您的应用程序的 lang/en/validation.php 文件中。如果您的应用程序没有 lang 目录,您可以通过运行 lang:publish Artisan 命令来让 Laravel 创建该目录。

lang/en/validation.php 文件中,您会找到每个验证规则的翻译条目。您可以根据应用程序的需求修改或更改这些消息。

此外,您还可以将此文件复制到其它语言目录,以便为您的应用程序的语言翻译这些消息。要了解更多关于 Laravel 本地化的信息,请查看完整的本地化文档。

默认情况下,Laravel 应用程序骨架并不包括 lang 目录。如果您想自定义 Laravel 的语言文件,您可以通过 lang:publish Artisan 命令来发布它们。

为特定属性定制消息

您可以在应用程序的验证语言文件中,为特定的属性和规则组合自定义错误消息。为此,您需要将您的消息定制添加到应用程序 lang/xx/validation.php 文件中的 custom 数组:

'custom' => [
    'email' => [
        'required' => '我们需要知道您的电子邮件地址!',
        'max' => '您的电子邮件地址太长了!'
    ],
],

在语言文件中指定属性

Laravel 内置的许多错误消息包含一个 :attribute 占位符,该占位符会被验证字段或属性的名称替换。如果您希望验证消息中的 :attribute 部分被自定义的值替换,您可以在 lang/xx/validation.php 语言文件的 attributes 数组中指定自定义的属性名称:

'attributes' => [
    'email' => '电子邮件地址',
],

默认情况下,Laravel 应用程序骨架不包含 lang 目录。如果您想自定义 Laravel 的语言文件,您可以通过 lang:publish Artisan 命令来发布它们。

在语言文件中指定值

Laravel 的一些内置验证规则错误消息包含一个 :value 占位符,该占位符会被请求属性的当前值替换。然而,有时您可能希望验证消息中的 :value 部分被自定义的值表示。例如,考虑以下规则,规定当 payment_typecc 时,credit_card_number 字段是必填的:

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

如果此验证规则失败,它将生成以下错误消息:

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

为了不显示 cc 作为支付类型值,您可以通过在 lang/xx/validation.php 语言文件中定义一个 values 数组,指定一个更用户友好的值表示:

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

默认情况下,Laravel 应用程序骨架不包含 lang 目录。如果您想自定义 Laravel 的语言文件,您可以通过 lang:publish Artisan 命令来发布它们。

定义了该值后,验证规则将生成以下错误消息:

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

可用的验证规则

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

accepted

验证字段必须是 "yes""on"1"1"true"true"。这对于验证“服务条款”接受或类似字段非常有用。

accepted_if:anotherfield,value,…​

当另一个正在验证的字段的值等于指定值时,验证字段必须是 "yes""on"1"1"true"true"。这对于验证 “服务条款” 接受或类似字段非常有用。

active_url

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

after:date

验证字段必须是一个在给定日期之后的值。日期将传递给 PHP 的 strtotime 函数,以便转换为有效的 DateTime 实例:

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

您可以指定另一个字段与日期进行比较,而不是传递一个日期字符串由 strtotime 函数进行评估:

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

after_or_equal:date

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

alpha

验证字段必须完全是 Unicode 字母字符,包含在 \p{L}\p{M} 中。

为了将此验证规则限制为 ASCII 范围内的字符(a-z 和 A-Z),可以在验证规则中提供 ascii 选项:

'username' => 'alpha:ascii',

alpha_dash

验证字段必须完全是 Unicode 字母数字字符,包含在 \p{L}\p{M}\p{N} 中,以及 ASCII 的短横线(-)和下划线(_)。

为了将此验证规则限制为 ASCII 范围内的字符(a-zA-Z),可以在验证规则中提供 ascii 选项:

'username' => 'alpha_dash:ascii',

alpha_num

验证字段必须完全是 Unicode 字母数字字符,包含在 \p{L}\p{M}\p{N} 中。

为了将此验证规则限制为 ASCII 范围内的字符(a-z 和 A-Z),可以在验证规则中提供 ascii 选项:

'username' => 'alpha_num:ascii',

array

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

当向数组规则提供额外的值时,输入数组中的每个键必须存在于规则提供的值列表中。在以下示例中,由于 admin 键不包含在提供给数组规则的值列表中,因此该字段无效:

use Illuminate\Support\Facades\Validator;

$input = [
    'user' => [
        'name' => 'Taylor Otwell',
        'username' => 'taylorotwell',
        'admin' => true,
    ],
];

Validator::make($input, [
    'user' => 'array:name,username',
]);

通常,您应始终指定允许在数组中出现的数组键。

ascii

验证字段必须完全是 7 位 ASCII 字符。

bail

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

虽然 bail 规则仅会在遇到验证失败时停止验证特定字段,但 stopOnFirstFailure 方法将告知验证器一旦出现单个验证失败,应停止验证所有属性:

if ($validator->stopOnFirstFailure()->fails()) {
    // ...
}

before:date

验证字段必须是一个在给定日期之前的值。日期将传递给 PHP 的 strtotime 函数,以便转换为有效的 DateTime 实例。此外,像 after 规则一样,可以将另一个字段的名称作为 date 的值。

before_or_equal:date

验证字段必须是一个在给定日期之前或相等的值。日期将传递给 PHP 的 strtotime 函数,以便转换为有效的 DateTime 实例。此外,像 after 规则一样,可以将另一个字段的名称作为 date 的值。

between:min,max

验证字段的大小必须在给定的最小值和最大值之间(包括最小值和最大值)。字符串、数字、数组和文件会像 size 规则一样进行评估。

boolean

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

confirmed

验证字段必须有一个匹配的字段 {field}_confirmation。例如,如果正在验证的字段是 password,则必须存在一个匹配的 password_confirmation 字段。

您还可以传递自定义的确认字段名。例如,confirmed:repeat_username 将期望 repeat_username 字段与正在验证的字段匹配。

contains:foo,bar,…​

验证字段必须是一个数组,并且必须包含所有给定的参数值。

current_password

验证字段必须与已认证用户的密码匹配。您可以使用规则的第一个参数指定身份验证守卫:

'password' => 'current_password:api'

date

验证字段必须是一个有效的、非相对的日期,符合 PHP 的 strtotime 函数。

date_equals:date

验证字段必须与给定的日期相等。日期将传递给 PHP 的 strtotime 函数,以便转换为有效的 DateTime 实例。

date_format:format,…​

验证字段必须与给定的格式匹配。您应该使用 datedate_format 验证字段,而不是同时使用两者。此验证规则支持 PHP DateTime 类支持的所有格式。

decimal:min,max

验证字段必须是数字,并且必须包含指定数量的小数位:

// 必须有两个小数位 (9.99)...
'price' => 'decimal:2'

// 必须有 2 到 4 个小数位...
'price' => 'decimal:2,4'

declined

验证字段必须是 "no""off"0"0"false"false"

declined_if:anotherfield,value,…​

当另一个正在验证的字段的值等于指定值时,验证字段必须是 "no""off"0"0"false"false"

different:field

验证字段的值必须与另一个字段的值不同。

digits:value

验证字段必须是一个具有精确长度 value 的整数。

digits_between:min,max

验证字段必须是一个整数,且其长度在给定的最小值和最大值之间。

dimensions

验证字段必须是一个符合规则指定维度约束的图像:

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

可用的约束有:min_widthmax_widthmin_heightmax_heightwidthheightratio

比例约束应表示为宽度除以高度。可以使用分数(如 3/2)或浮动数(如 1.5)来指定:

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

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

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

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

distinct

在验证数组时,验证字段不能有重复的值:

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

distinct 默认使用宽松的变量比较。要使用严格比较,可以在验证规则定义中添加 strict 参数:

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

您可以在验证规则的参数中添加 ignore_case 以忽略大小写差异:

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

doesnt_start_with:foo,bar,…​

验证字段的值不能以给定的值之一开始。

doesnt_end_with:foo,bar,…​

验证字段的值不能以给定的值之一结束。

email

验证字段必须是一个格式正确的电子邮件地址。此验证规则使用 egulias/email-validator 包来验证电子邮件地址。默认情况下,应用 RFCValidation 验证器,但也可以应用其他验证样式:

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

上面的例子会同时应用 RFCValidationDNSCheckValidation 验证。以下是可以应用的完整验证样式列表:

  • rfc: RFCValidation

  • strict: NoRFCWarningsValidation

  • dns: DNSCheckValidation

  • spoof: SpoofCheckValidation

  • filter: FilterEmailValidation

  • filter_unicode: FilterEmailValidation::unicode()

filter 验证器,它使用 PHP 的 filter_var 函数,随 Laravel 一起提供,并且在 Laravel 5.8 版本之前是 Laravel 默认的电子邮件验证行为。

dnsspoof 验证器需要 PHP 的 intl 扩展。

ends_with:foo,bar,…​

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

enum

Enum 规则是一个基于类的规则,用于验证字段是否包含有效的枚举值。Enum 规则接受枚举类的名称作为唯一的构造参数。当验证原始值时,应该提供一个带有值的枚举:

use App\Enums\ServerStatus;
use Illuminate\Validation\Rule;

$request->validate([
    'status' => [Rule::enum(ServerStatus::class)],
]);

Enum 规则的 onlyexcept 方法可用于限制哪些枚举值应该被认为是有效的:

Rule::enum(ServerStatus::class)
    ->only([ServerStatus::Pending, ServerStatus::Active]);

Rule::enum(ServerStatus::class)
    ->except([ServerStatus::Pending, ServerStatus::Active]);

when 方法可用于有条件地修改 Enum 规则:

use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;

Rule::enum(ServerStatus::class)
    ->when(
        Auth::user()->isAdmin(),
        fn ($rule) => $rule->only(...),
        fn ($rule) => $rule->only(...),
    );

exclude

验证字段将被排除在 validatevalidated 方法返回的请求数据之外。

exclude_if:anotherfield,value

如果 anotherfield 字段等于 value,则验证字段将被排除在 validatevalidated 方法返回的请求数据之外。

如果需要复杂的条件排除逻辑,可以使用 Rule::excludeIf 方法。该方法接受一个布尔值或一个闭包。当传入闭包时,闭包应返回 truefalse,以指示是否应排除字段:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

Validator::make($request->all(), [
    'role_id' => Rule::excludeIf($request->user()->is_admin),
]);

Validator::make($request->all(), [
    'role_id' => Rule::excludeIf(fn () => $request->user()->is_admin),
]);

exclude_unless:anotherfield,value

如果 anotherfield 字段不等于 value,则验证字段将被排除在 validatevalidated 方法返回的请求数据之外。如果 valuenull (exclude_unless:name,null),则字段将被排除,除非比较字段为 null 或比较字段在请求数据中缺失。

exclude_with:anotherfield

如果 anotherfield 字段存在,则验证字段将被排除在 validatevalidated 方法返回的请求数据之外。

exclude_without:anotherfield

如果 anotherfield 字段不存在,则验证字段将被排除在 validatevalidated 方法返回的请求数据之外。

exists:table,column

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

存在规则的基本用法

'state' => 'exists:states'

如果没有指定 column 选项,字段名称将被使用。因此,在这种情况下,该规则将验证 states 数据表中是否包含与请求的 state 属性值匹配的记录。

指定自定义列名

可以显式指定要用于验证规则的数据库列名,将其放置在数据库表名称之后:

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

有时,可能需要指定执行 exists 查询时使用的特定数据库连接。可以通过在表名称前面加上连接名称来实现:

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

而不是直接指定表名称,您还可以指定应使用的 Eloquent 模型,以确定表名称:

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

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

use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

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

您可以通过将列名作为第二个参数传递给 exists 方法,显式指定 Rule::exists 生成的规则应使用的数据库列名:

'state' => Rule::exists('states', 'abbreviation'),

extensions:foo,bar,…​

验证文件必须具有一个用户指定的扩展名,该扩展名必须与列出的扩展名之一相符:

'photo' => ['required', 'extensions:jpg,png']

你不应该仅仅依赖于通过用户指定的扩展名验证文件。此规则通常应与 mimesmimetypes 规则一起使用。

file

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

filled

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

gt:field

验证字段必须大于给定的字段或值。两个字段必须是相同类型。字符串、数字、数组和文件将按照与 size 规则相同的惯例进行评估。

gte:field

验证字段必须大于或等于给定的字段或值。两个字段必须是相同类型。字符串、数字、数组和文件将按照与 size 规则相同的惯例进行评估。

hex_color

验证字段必须包含一个有效的十六进制颜色值。

image

验证上传的文件必须是图像(jpg、jpeg、png、bmp、gif、svg 或 webp)。

in:foo,bar,…​

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

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

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

in 规则与 array 规则结合使用时,输入数组中的每个值必须包含在提供给 in 规则的值列表中。例如,下面的 LAS 机场代码在输入数组中无效,因为它没有包含在 in 规则提供的机场列表中:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

$input = [
    'airports' => ['NYC', 'LAS'],
];

Validator::make($input, [
    'airports' => [
        'required',
        'array',
    ],
    'airports.*' => Rule::in(['NYC', 'LIT']),
]);

in_array:anotherfield.*

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

integer

验证字段必须是一个整数。

此验证规则不验证输入是否为 “整数” 变量类型,只验证输入是否符合 PHP 的 FILTER_VALIDATE_INT 规则。如果您需要验证输入为数字类型,可以将此规则与 numeric 验证规则结合使用。

ip

验证字段必须是一个 IP 地址。

ipv4

验证字段必须是一个 IPv4 地址。

ipv6

验证字段必须是一个 IPv6 地址。

json

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

lt:field

验证字段必须小于给定字段。两个字段必须是相同类型。字符串、数字、数组和文件将按照与 size 规则相同的惯例进行评估。

lte:field

验证字段必须小于或等于给定字段。两个字段必须是相同类型。字符串、数字、数组和文件将按照与 size 规则相同的惯例进行评估。

lowercase

验证字段必须是小写字母。

max:value

验证字段的值不得大于给定的最大值。这个规则可以用于字符串、数字、数组和文件。对于字符串,它检查字符的数量;对于数字,它检查数值;对于数组,它检查元素的数量;对于文件,它检查文件的大小。

max_digits:value

待验证的整数必须具有最大值长度为 value。

mimetypes:text/plain,…​

待验证的文件必须匹配以下给定的 MIME 类型:

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

为了确定上传文件的 MIME 类型,将读取文件的内容,并且框架会尝试猜测 MIME 类型,这可能与客户端提供的 MIME 类型不同。

mimes:foo,bar,…​

验证的文件必须具有与列出的扩展名对应的 MIME 类型:

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

尽管您只需要指定扩展名,但此规则实际上是通过读取文件的内容并猜测其 MIME 类型来验证文件的 MIME 类型。MIME 类型及其对应扩展名的完整列表可以在以下位置找到:

MIME 类型和扩展名

此验证规则不会验证 MIME 类型与用户分配给文件的扩展名是否一致。例如,mimes:png 验证规则会将一个包含有效 PNG 内容的文件视为有效的 PNG 图像,即使该文件的名称是 photo.txt。如果您希望验证用户分配给文件的扩展名,可以使用 extensions 规则。

min:value

验证字段的值必须具有最小值。字符串、数字、数组和文件的评估与 size 规则相同。

min_digits:value

验证的整数必须具有至少 value 位长度。

multiple_of:value

验证字段的值必须是 value 的倍数。

missing

验证字段必须在输入数据中不存在。

missing_if:anotherfield,value,…​

如果 anotherfield 字段等于任何指定的值,则验证字段在输入数据中必须不存在。

missing_unless:anotherfield,value

除非 anotherfield 字段等于任何指定的值,否则验证字段在输入数据中必须不存在。

missing_with:foo,bar,…​

仅当其他指定字段存在时,验证字段必须在输入数据中不存在。

missing_with_all:foo,bar,…​

仅当所有其他指定字段都存在时,验证字段必须在输入数据中不存在。

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

验证字段必须是【数字】。

present

验证字段必须存在于输入数据中。

present_if:anotherfield,value,…​

如果 anotherfield 字段等于任何指定的值,则验证字段必须存在于输入数据中。

present_unless:anotherfield,value

除非 anotherfield 字段等于任何指定的值,否则验证字段必须存在于输入数据中。

present_with:foo,bar,…​

仅当其他指定字段存在时,验证字段必须存在于输入数据中。

present_with_all:foo,bar,…​

仅当所有其他指定字段都存在时,验证字段必须存在于输入数据中。

prohibited

验证字段必须缺失或为空。如果字段为空,则满足以下条件之一:

  • 值为 null

  • 值为空字符串。

  • 值为空数组或空的 Countable 对象。

  • 值为路径为空的上传文件。

prohibited_if:anotherfield,value,…​

如果 anotherfield 字段等于任何指定的值,则验证字段必须缺失或为空。如果字段为空,则满足以下条件之一:

  • 值为 null

  • 值为空字符串。

  • 值为空数组或空的 Countable 对象。

  • 值为路径为空的上传文件。

如果需要复杂的条件禁止逻辑,可以使用 Rule::prohibitedIf 方法。此方法接受一个布尔值或闭包。当给定闭包时,闭包应返回 truefalse,指示验证字段是否应被禁止:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

Validator::make($request->all(), [
    'role_id' => Rule::prohibitedIf($request->user()->is_admin),
]);

Validator::make($request->all(), [
    'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin),
]);

prohibited_unless:anotherfield,value,…​

除非 anotherfield 字段等于任何指定的值,否则验证字段必须缺失或为空。如果字段为空,则满足以下条件之一:

  • 值为 null

  • 值为空字符串。

  • 值为空数组或空的 Countable 对象。

  • 值为路径为空的上传文件。

prohibits:anotherfield,…​

如果验证字段不缺失或为空,则 anotherfield 中的所有字段必须缺失或为空。如果字段为空,则满足以下条件之一:

  • 值为 null

  • 值为空字符串。

  • 值为空数组或空的 Countable 对象。

  • 值为路径为空的上传文件。

regex:pattern

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

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

在使用 regex / not_regex 模式时,可能需要使用数组而不是 | 分隔符来指定验证规则,尤其是当正则表达式包含 | 字符时。

required

验证字段必须出现在输入数据中且不能为空。如果字段为空,则满足以下条件之一:

  • 值为 null

  • 值为空字符串。

  • 值为空数组或空的 Countable 对象。

  • 值为路径为空的上传文件。

required_if:anotherfield,value,…​

如果 anotherfield 字段等于任何指定的值,则验证字段必须出现在输入数据中且不能为空。

如果您希望为 required_if 规则构造更复杂的条件,可以使用 Rule::requiredIf 方法。此方法接受一个布尔值或闭包。当传递闭包时,闭包应返回 true 或 false,指示验证字段是否是必填的:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

Validator::make($request->all(), [
    'role_id' => Rule::requiredIf($request->user()->is_admin),
]);

Validator::make($request->all(), [
    'role_id' => Rule::requiredIf(fn () => $request->user()->is_admin),
]);

required_if_accepted:anotherfield,…​

如果 anotherfield 字段等于 "yes""on"1"1"true"true",则验证字段必须出现在输入数据中且不能为空。

required_if_declined:anotherfield,…​

如果 anotherfield 字段等于 "no""off"0"0"false"false",则验证字段必须出现在输入数据中且不能为空。

required_unless:anotherfield,value,…​

除非 anotherfield 字段等于任何指定的值,否则验证字段必须出现在输入数据中且不能为空。这也意味着 unless 字段必须出现在请求数据中,除非值为 null。如果值为 nullrequired_unless:name,null),则验证字段在比较字段为 null 或比较字段缺失时才是必填项。

required_with:foo,bar,…​

仅当其他指定字段存在且不为空时,验证字段必须出现在输入数据中且不能为空。

required_with_all:foo,bar,…​

仅当所有其他指定字段都存在且不为空时,验证字段必须出现在输入数据中且不能为空。

required_without:foo,bar,…​

仅当其他指定字段为空或缺失时,验证字段必须出现在输入数据中且不能为空。

required_without_all:foo,bar,…​

仅当所有其他指定字段为空或缺失时,验证字段必须出现在输入数据中且不能为空。

required_array_keys:foo,bar,…​

验证字段必须是一个数组,并且必须包含至少指定的键。

same:field

验证字段必须与指定字段的值相同。

size:value

验证字段必须具有与给定值相匹配的大小。对于字符串数据,值对应字符数;对于数字数据,值对应给定的整数值(该属性还必须具有 numericinteger 规则);对于数组,size 对应数组的元素个数;对于文件,size 对应文件大小(以千字节为单位)。例如:

// 验证字符串长度正好为12个字符...
'title' => 'size:12';

// 验证提供的整数值为10...
'seats' => 'integer|size:10';

// 验证数组正好有5个元素...
'tags' => 'array|size:5';

// 验证上传的文件正好为512千字节...
'image' => 'file|size:512';

starts_with:foo,bar,…​

验证字段必须以给定的某个值开头。

string

验证字段必须是字符串。如果您希望字段也可以为空,则应为该字段指定 nullable 规则。

timezone

验证字段必须是一个有效的时区标识符,符合 DateTimeZone::listIdentifiers 方法的要求。

DateTimeZone::listIdentifiers 方法接受的参数也可以提供给此验证规则:

'timezone' => 'required|timezone:all';

'timezone' => 'required|timezone:Africa';

'timezone' => 'required|timezone:per_country,US';

unique:table,column

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

指定自定义表/列名称:

您可以指定 Eloquent 模型,以便根据模型确定表名:

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

column 选项可用于指定字段对应的数据库列。如果没有指定 column 选项,则会使用正在验证的字段名称作为列名。

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

指定自定义数据库连接

有时,您可能需要为验证器执行的数据库查询设置自定义连接。为此,您可以将连接名称作为前缀添加到表名:

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

强制唯一规则忽略给定的 ID

有时,您可能希望在唯一验证时忽略给定的 ID。例如,考虑一个 “更新资料” 页面,其中包括用户的姓名、电子邮件地址和位置。您可能希望验证电子邮件地址是否唯一。然而,如果用户只更改了姓名字段,而没有更改电子邮件字段,则不希望因为该用户已经拥有该电子邮件地址而抛出验证错误。

为了指示验证器忽略用户的 ID,我们可以使用 Rule 类流畅地定义规则。在这个例子中,我们还指定了验证规则作为数组,而不是使用 | 字符分隔规则:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

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

您永远不应该将任何用户控制的请求输入传递给 ignore 方法。相反,您应该仅传递系统生成的唯一 ID,如自增 ID 或来自 Eloquent 模型实例的 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_id 列值为 1 的记录:

'email' => Rule::unique('users')->where(fn (Builder $query) => $query->where('account_id', 1))

uppercase

正在验证的字段必须是大写。

url

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

如果您希望指定应该被认为有效的 URL 协议,您可以将协议作为验证规则参数传递:

'url' => 'url:http,https',
'game' => 'url:minecraft,steam',

ulid

正在验证的字段必须是有效的通用唯一字典顺序可排序标识符(ULID)。

uuid

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

有条件地添加规则

跳过某些字段的验证

有时,您可能希望在某个字段具有特定值时跳过验证。可以使用 exclude_if 验证规则来实现这一点。例如,当 has_appointment 字段的值为 false 时,appointment_datedoctor_name 字段将不会被验证:

use Illuminate\Support\Facades\Validator;

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

另外,您可以使用 exclude_unless 规则,仅在某个字段具有特定值时才验证给定字段:

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

当字段存在时验证

在某些情况下,您可能希望仅在字段存在时对其进行验证。为此,可以将 sometimes 规则添加到您的规则列表中:

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

在上面的例子中,只有当 email 字段存在于 $data 数组中时,才会对其进行验证。

如果您尝试验证一个字段,该字段应该始终存在但可能为空,请查看【关于可选字段的说明】。

复杂条件验证

有时,您可能希望根据更复杂的条件逻辑添加验证规则。例如,您希望仅在另一个字段的值大于 100 时,才要求某个字段为必填。或者,当某个字段存在时,可能需要两个字段的值满足特定要求。添加这些验证规则不必麻烦。首先,使用 静态规则 创建一个 Validator 实例,这些规则不会改变:

use Illuminate\Support\Facades\Validator;

$validator = Validator::make($request->all(), [
    'email' => 'required|email',
    'games' => 'required|numeric',
]);

假设我们的 web 应用是面向游戏收藏家的。如果一个游戏收藏家注册到我们的应用,并且他们拥有超过 100 款游戏,我们希望他们解释为什么拥有这么多游戏。例如,或许他们经营一个游戏转售店,或者只是单纯喜欢收藏游戏。为了有条件地添加这一要求,我们可以在 Validator 实例上使用 sometimes 方法。

use Illuminate\Support\Fluent;

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

传递给 sometimes 方法的第一个参数是我们有条件验证的字段名。第二个参数是我们要添加的规则列表。如果作为第三个参数传递的闭包返回 true,则规则将被添加。这种方法使得构建复杂的条件验证变得轻松。您甚至可以同时为多个字段添加条件验证:

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

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

复杂的条件数组验证

有时,您可能希望根据同一嵌套数组中的另一个字段来验证一个字段,但您并不知道该字段的索引。在这种情况下,您可以允许闭包接收第二个参数,该参数是正在验证的当前数组项:

$input = [
    'channels' => [
        [
            'type' => 'email',
            'address' => 'abigail@example.com',
        ],
        [
            'type' => 'url',
            'address' => 'https://example.com',
        ],
    ],
];

$validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) {
    return $item->type === 'email';
});

$validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) {
    return $item->type !== 'email';
});

与传递给闭包的 $input 参数类似,当属性数据是一个数组时,传递给闭包的 $item 参数是一个 Illuminate\Support\Fluent 实例;否则,它是一个字符串。

验证数组

如在数组【验证规则文档】中所讨论,array 规则接受一个允许的数组键的列表。如果数组中存在任何额外的键,则验证将失败:

use Illuminate\Support\Facades\Validator;

$input = [
    'user' => [
        'name' => 'Taylor Otwell',
        'username' => 'taylorotwell',
        'admin' => true,
    ],
];

Validator::make($input, [
    'user' => 'array:name,username',
]);

一般而言,您应该始终指定允许存在于数组中的键。否则,验证器的 validatevalidated 方法将返回所有已验证的数据,包括数组及其所有键,即使这些键没有被其它嵌套的数组验证规则验证过。

验证嵌套数组输入

验证嵌套数组形式输入字段不必困难。您可以使用 “点符号” 来验证数组中的属性。例如,如果传入的 HTTP 请求包含 photos[profile] 字段,您可以像下面这样进行验证:

use Illuminate\Support\Facades\Validator;

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

您也可以验证数组中的每个元素。例如,要验证给定数组输入字段中的每个电子邮件是否唯一,您可以这样做:

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

同样,您可以【在语言文件中为数组字段指定自定义验证消息】时使用 * 字符,这使得为基于数组的字段使用单一验证消息变得轻而易举:

'custom' => [
    'person.*.email' => [
        'unique' => '每个人必须拥有唯一的电子邮件地址',
    ]
],

访问嵌套数组数据

有时,您可能需要在为属性分配验证规则时访问给定嵌套数组元素的值。您可以使用 Rule::forEach 方法来实现这一点。forEach 方法接受一个闭包,该闭包将在验证时为数组属性的每次迭代调用,并将接收属性的值以及完全展开的属性名。闭包应返回一个规则数组,用于分配给数组元素:

use App\Rules\HasPermission;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

$validator = Validator::make($request->all(), [
    'companies.*.id' => Rule::forEach(function (string|null $value, string $attribute) {
        return [
            Rule::exists(Company::class, 'id'),
            new HasPermission('manage-company', $value),
        ];
    }),
]);

错误消息的索引和位置

在验证数组时,您可能希望在应用程序显示的错误消息中引用某个失败验证项的索引或位置。为了实现这一点,您可以在【自定义验证消息】中包含 :index(从 0 开始)和 :position(从 1 开始)占位符:

use Illuminate\Support\Facades\Validator;

$input = [
    'photos' => [
        [
            'name' => 'BeachVacation.jpg',
            'description' => 'A photo of my beach vacation!',
        ],
        [
            'name' => 'GrandCanyon.jpg',
            'description' => '',
        ],
    ],
];

Validator::validate($input, [
    'photos.*.description' => 'required',
], [
    'photos.*.description.required' => 'Please describe photo #:position.',
]);

在上面的例子中,验证失败时,用户将看到以下错误消息:“Please describe photo #2”

如果需要,您还可以通过第二个索引、第二个位置、第三个索引、第三个位置等来引用更深层次的嵌套索引和位置。

例如:

'photos.*.attributes.*.string' => 'Invalid attribute for photo #:second-position.',

验证文件

Laravel 提供了多种验证规则,可用于验证上传的文件,如 mimesimageminmax。虽然您可以单独指定这些规则来验证文件,但 Laravel 还提供了一个流畅的文件验证规则构建器,您可能会觉得它很方便:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rules\File;

Validator::validate($input, [
    'attachment' => [
        'required',
        File::types(['mp3', 'wav'])
            ->min(1024)
            ->max(12 * 1024),
    ],
]);

如果您的应用程序接受用户上传的图像,您可以使用 File 规则的 image 构造方法来表示上传的文件应该是一个图像。此外,可以使用 dimensions 规则来限制图像的尺寸:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\File;

Validator::validate($input, [
    'photo' => [
        'required',
        File::image()
            ->min(1024)
            ->max(12 * 1024)
            ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)),
    ],
]);

有关验证图像尺寸的更多信息,请参阅【尺寸规则文档】。

文件大小

为了方便,最小和最大文件大小可以作为带有单位后缀的字符串来指定。支持 kbmbgbtb 后缀:

File::image()
    ->min('1kb')
    ->max('10mb')

文件类型

即使在调用 types 方法时只需要指定扩展名,该方法实际上通过读取文件内容并猜测其 MIME 类型来验证文件的 MIME 类型。有关 MIME 类型及其相应扩展名的完整列表,请参见以下链接:

验证密码

为了确保密码具有足够的复杂性,您可以使用 Laravel 的 Password 规则对象:

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rules\Password;

$validator = Validator::make($request->all(), [
    'password' => ['required', 'confirmed', Password::min(8)],
]);

Password 规则对象允许您轻松定制应用程序的密码复杂性要求,例如指定密码至少包含一个字母、数字、符号或混合大小写的字符:

// 至少 8 个字符...
Password::min(8)

// 至少一个字母...
Password::min(8)->letters()

// 至少一个大写字母和一个小写字母...
Password::min(8)->mixedCase()

// 至少一个数字...
Password::min(8)->numbers()

// 至少一个符号...
Password::min(8)->symbols()

此外,您可以使用 uncompromised 方法确保密码未在公共密码数据泄露中被泄露:

Password::min(8)->uncompromised()

内部,Password 规则对象使用 k-匿名性 模型来判断密码是否已通过 haveibeenpwned.com 服务泄露,而不会牺牲用户的隐私或安全性。

默认情况下,如果密码至少出现在一次数据泄露中,它将被视为已泄露。您可以使用 uncompromised 方法的第一个参数自定义此阈值:

// 确保密码在同一数据泄露中出现少于 3 次...
Password::min(8)->uncompromised(3);

当然,您可以链式调用上述方法:

Password::min(8)
    ->letters()
    ->mixedCase()
    ->numbers()
    ->symbols()
    ->uncompromised()

定义默认密码规则

您可能希望在应用程序的某个位置指定密码的默认验证规则。您可以使用 Password::defaults 方法轻松实现这一点,该方法接受一个闭包。传递给 defaults 方法的闭包应该返回密码规则的默认配置。通常,defaults 规则应该在应用程序的某个服务提供者的 boot 方法中调用:

use Illuminate\Validation\Rules\Password;

/**
 * 启动任何应用程序服务。
 */
public function boot(): void
{
    Password::defaults(function () {
        $rule = Password::min(8);

        return $this->app->isProduction()
                    ? $rule->mixedCase()->uncompromised()
                    : $rule;
    });
}

然后,当您希望将默认规则应用于某个正在验证的密码时,可以调用 defaults 方法,不传递任何参数:

'password' => ['required', Password::defaults()],

有时,您可能希望将额外的验证规则附加到默认的密码验证规则中。您可以使用 rules 方法来实现这一点:

use App\Rules\ZxcvbnRule;

Password::defaults(function () {
    $rule = Password::min(8)->rules([new ZxcvbnRule]);

    // ...
});

自定义验证规则

使用规则对象

Laravel 提供了多种有用的验证规则;然而,您可能希望指定一些自定义规则。注册自定义验证规则的一种方法是使用规则对象。要生成一个新的规则对象,您可以使用 make:rule Artisan 命令。我们将使用此命令生成一个验证字符串是否为大写的规则。Laravel 会将新规则放置在 app/Rules 目录下。如果该目录不存在,Laravel 会在您执行 Artisan 命令创建规则时自动创建它:

php artisan make:rule Uppercase

一旦规则创建完成,您就可以定义其行为了。规则对象包含一个方法:validate。此方法接收属性名、其值以及在验证失败时应该调用的回调函数,回调函数会传递验证错误消息:

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class Uppercase implements ValidationRule
{
    /**
     * 运行验证规则。
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (strtoupper($value) !== $value) {
            $fail('The :attribute must be uppercase.');
        }
    }
}

一旦定义了规则,您可以通过将规则对象的实例与其它验证规则一起传递,来将规则附加到验证器上:

use App\Rules\Uppercase;

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

翻译验证消息

你可以选择在 $fail 闭包中提供一个翻译字符串键,而不是提供字面上的错误消息,并指示 Laravel 翻译该错误消息。

if (strtoupper($value) !== $value) {
    $fail('validation.uppercase')->translate();
}

如果需要,您可以向 translate 方法的第一个和第二个参数提供占位符替换和首选语言:

$fail('validation.location')->translate([
    'value' => $this->value,
], 'fr');

访问额外的数据

如果您的自定义验证规则类需要访问所有其它正在验证的数据,您的规则类可以实现 Illuminate\Contracts\Validation\DataAwareRule 接口。该接口要求您的类定义一个 setData 方法。Laravel 会在验证开始之前自动调用此方法,并将所有验证数据传递给它:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\ValidationRule;

class Uppercase implements DataAwareRule, ValidationRule
{
    /**
     * 所有正在验证的数据。
     *
     * @var array<string, mixed>
     */
    protected $data = [];

    // ...

    /**
     * 设置正在验证的数据。
     *
     * @param  array<string, mixed>  $data
     */
    public function setData(array $data): static
    {
        $this->data = $data;

        return $this;
    }
}

或者,如果您的验证规则需要访问执行验证的验证器实例,您可以实现 ValidatorAwareRule 接口:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Validation\Validator;

class Uppercase implements ValidationRule, ValidatorAwareRule
{
    /**
     * 验证器实例。
     *
     * @var \Illuminate\Validation\Validator
     */
    protected $validator;

    // ...

    /**
     * 设置当前验证器。
     */
    public function setValidator(Validator $validator): static
    {
        $this->validator = $validator;

        return $this;
    }
}

使用闭包

如果您只需要在应用程序中使用一次自定义规则,可以使用闭包,而不是规则对象。闭包接收属性的名称、属性的值以及一个 $fail 回调函数,如果验证失败,该回调函数将被调用:

use Illuminate\Support\Facades\Validator;
use Closure;

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

在上面的例子中,如果 title 字段的值为 'foo',验证将失败,并返回自定义的错误消息:“The title is invalid.”

隐式规则

默认情况下,当被验证的属性不存在或包含空字符串时,正常的验证规则(包括自定义规则)不会被执行。例如,unique 规则不会对空字符串进行验证:

use Illuminate\Support\Facades\Validator;

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

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

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

为了确保自定义规则在属性为空时仍然运行,规则必须暗示该属性是必填的。要快速生成一个新的隐式规则对象,您可以使用 make:rule Artisan 命令,并加上 --implicit 选项:

php artisan make:rule Uppercase --implicit

“隐式” 规则仅表示该属性是必填的。是否实际使缺失或空的属性无效,取决于您如何实现规则。