简介

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 方法中。不用担心——所有可用的验证规则都有文档说明。再次强调,如果验证失败,系统会自动生成适当的响应;如果验证通过,控制器将继续正常执行。

此外,您也可以使用 validateWithBag 方法来验证请求,并将错误信息保存在指定的错误袋中:

$validated = $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 会自动将用户重定向回之前的页面。此外,所有验证错误以及请求输入都会自动闪存到会话中。

由 web 中间件组提供的 Illuminate\View\Middleware\ShareErrorsFromSession 中间件会将 $errors 变量共享给应用程序的所有视图。当应用此中间件后,视图中始终可用 $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 会将 TrimStrings 和 ConvertEmptyStringsToNull 中间件包含在应用程序的全局中间件栈中。因此,如果您不希望校验器将 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 field must be a string. (and 4 more errors)",
    "errors": {
        "team_name": [
            "The team name field must be a string.",
            "The team name field 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 field must be a valid email address."
        ]
    }
}

表单请求校验

创建表单请求

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

php artisan make:request StorePostRequest

生成的表单请求类会放置在 app/Http/Requests 目录中。如果该目录不存在,运行 make:request 命令时会创建该目录。Laravel 生成的每个表单请求都包含两个方法:authorize 和 rules。

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

/**
 * Get the validation rules that apply to the request.
 *
 * @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 服务容器自动进行解析。

那么,校验规则是如何执行的呢?您只需在控制器方法上对请求进行类型提示即可。传入的表单请求会在调用控制器方法之前完成校验,这意味着您无需在控制器中添加任何校验逻辑:

/**
 * Store a new blog post.
 */
public function store(StorePostRequest $request): RedirectResponse
{
    // The incoming request is valid...

    // Retrieve the validated input data...
    $validated = $request->validated();

    // Retrieve a portion of the validated input data...
    $validated = $request->safe()->only(['name', 'email']);
    $validated = $request->safe()->except(['name', 'email']);

    // Store the blog post...

    return redirect('/posts');
}

如果校验失败,将生成重定向响应,将用户引导回之前的页面。错误信息也会被闪存到会话中,以便后续展示。如果请求是 XHR 请求,则会向用户返回状态码为 422 的 HTTP 响应,其中包含校验错误的 JSON 表示。

需要为基于 Inertia 的 Laravel 前端添加实时表单请求校验?请查看 Laravel Precognition。

执行附加校验

有时,您需要在完成初始校验后执行附加校验。您可以通过表单请求的 after 方法来实现这一点。

after 方法应返回一个可调用对象或闭包数组,这些可调用对象或闭包将在校验完成后被调用。传入的可调用对象将接收一个 Illuminate\Validation\Validator 实例,以便您在必要时抛出额外的错误信息:

use Illuminate\Validation\Validator;

/**
 * Get the "after" validation callables for the request.
 */
public function after(): array
{
    return [
        function (Validator $validator) {
            if ($this->somethingElseIsInvalid()) {
                $validator->errors()->add(
                    'field',
                    'Something is wrong with this field!'
                );
            }
        }
    ];
}

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

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

/**
 * Get the "after" validation callables for the request.
 */
public function after(): array
{
    return [
        new ValidateUserStatus,
        new ValidateShippingTime,
        function (Validator $validator) {
            //
        }
    ];
}

在首次校验失败时停止

通过在请求类中添加 StopOnFirstFailure 属性,您可以通知校验器,一旦出现单次校验失败,就停止校验所有属性:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\Attributes\StopOnFirstFailure;
use Illuminate\Foundation\Http\FormRequest;

#[StopOnFirstFailure]
class StorePostRequest extends FormRequest
{
    // ...
}

未知字段校验失败

通过在请求类中添加 FailOnUnknownFields 属性,您可以指示 Laravel 拒绝任何未被请求校验规则定义的传入字段:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\Attributes\FailOnUnknownFields;
use Illuminate\Foundation\Http\FormRequest;

#[FailOnUnknownFields]
class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title' => ['required', 'string'],
            'body' => ['required', 'string'],
        ];
    }
}

您也可以在 AppServiceProvider 中为所有表单请求全局启用此行为:

use Illuminate\Foundation\Http\FormRequest;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    FormRequest::failOnUnknownFields();
}

如果需要,您可以将 false 传递给该属性,从而为特定请求禁用此行为:

#[FailOnUnknownFields(false)]
class PublicWebhookRequest extends FormRequest
{
    // ...
}

拒绝未知字段可以防止意外的输入键进一步流入应用程序,从而为防止批量赋值类问题提供额外保护。不过,您仍应配置模型的 $fillable / $guarded 属性,并且只持久化受信任且经过校验的输入。

自定义重定向位置

当表单请求验证失败时,系统会生成一个重定向响应,将用户重定向回之前的页面。不过,您可以自由定制此行为。要实现这一点,您可以在表单请求中使用 RedirectTo 特性:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\Attributes\RedirectTo;
use Illuminate\Foundation\Http\FormRequest;

#[RedirectTo('/dashboard')]
class StorePostRequest extends FormRequest
{
    // ...
}

或者,如果您希望将用户重定向到命名路由,可以使用 RedirectToRoute 特性:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\Attributes\RedirectToRoute;
use Illuminate\Foundation\Http\FormRequest;

#[RedirectToRoute('dashboard')]
class StorePostRequest extends FormRequest
{
    // ...
}

定制错误包

当表单请求验证失败时,错误信息会被存入 default 错误包。如果您需要将错误存入其他命名错误包,可以在表单请求中使用 ErrorBag 特性:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\Attributes\ErrorBag;
use Illuminate\Foundation\Http\FormRequest;

#[ErrorBag('login')]
class LoginRequest extends FormRequest
{
    // ...
}

表单请求授权

表单请求类还包含 authorize 方法。在该方法中,您可以判断已认证用户是否确实具有更新指定资源的权限。例如,您可以判断用户是否确实拥有其尝试更新的博客评论。通常,您会在此方法中与 授权门和策略进行交互:

use App\Models\Comment;

/**
 * Determine if the user is authorized to make this request.
 */
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}');

因此,如果您的应用使用了 路由模型绑定,那么通过将解析后的模型作为请求的属性来访问,代码将会变得更加简洁:

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

如果 authorize 方法返回 false,系统会自动返回状态码为 403 的 HTTP 响应,并且不会执行控制器方法。

如果您打算在应用的其他部分处理请求的授权逻辑,您可以完全移除 authorize 方法,或者直接返回 true:

/**
 * Determine if the user is authorized to make this request.
 */
public function authorize(): bool
{
    return true;
}

您可以在 authorize 方法的签名中对所需的任何依赖项进行类型提示。这些依赖项将通过 Laravel 服务容器自动解析。

定制错误消息

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

/**
 * Get the error messages for the defined validation rules.
 *
 * @return array<string, string>
 */
public function messages(): array
{
    return [
        'title.required' => 'A title is required',
        'body.required' => 'A message is required',
    ];
}

定制验证属性

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

/**
 * Get custom attributes for validator errors.
 *
 * @return array<string, string>
 */
public function attributes(): array
{
    return [
        'email' => 'email address',
    ];
}

为验证准备输入

如果您需要在应用验证规则之前,对请求中的任何数据进行准备或清洗,可以使用 prepareForValidation 方法:

use Illuminate\Support\Str;

/**
 * Prepare the data for validation.
 */
protected function prepareForValidation(): void
{
    $this->merge([
        'slug' => Str::slug($this->slug),
    ]);
}

同样地,如果在验证完成后需要对任何请求数据进行标准化处理,可以使用 passedValidation 方法:

/**
 * Handle a passed validation attempt.
 */
protected function passedValidation(): void
{
    $this->replace(['name' => 'Taylor']);
}

手动创建验证器

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

<?php

namespace App\Http\Controllers;

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

class PostController extends Controller
{
    /**
     * Store a new blog post.
     */
    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();
        }

        // Retrieve the validated input...
        $validated = $validator->validated();

        // Retrieve a portion of the validated input...
        $validated = $validator->safe()->only(['name', 'email']);
        $validated = $validator->safe()->except(['name', 'email']);

        // Store the blog post...

        return redirect('/posts');
    }
}

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

在确定请求验证是否失败后,您可以使用 withErrors 方法将错误消息存入会话。使用此方法后,重定向时 $errors 变量会自动共享给您的视图,以便轻松向用户展示这些错误。withErrors 方法接受一个验证器、一个 MessageBag 或一个 PHP array。

首次验证失败时停止

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 的一个实例。该对象提供了 only、except 和 all 方法,用于检索经过验证的数据子集或整个经过验证的数据数组:

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

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

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

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

// Validated data may be iterated...
foreach ($request->safe() as $key => $value) {
    // ...
}

// Validated data may be accessed as an array...
$validated = $request->safe();

$email = $validated['email'];

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

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

如果您想将经过验证的数据作为集合(collection)实例来检索,可以调用 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' => 'We need to know your email address!',
        'max' => 'Your email address is too long!'
    ],
],

在语言文件中指定属性

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

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

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

在语言文件中指定值

Laravel 的部分内置验证规则错误消息中包含 :value 占位符,该占位符会被替换为请求属性的当前值。然而,有时你可能希望验证消息中的 :value 部分替换为该值的自定义表示形式。例如,考虑以下规则:当 payment_type 的值为 cc 时,要求必须填写信用卡号:

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

若该验证规则失败,将生成如下错误消息:

当支付类型为 cc 时,信用卡号字段为必填项。

若要显示更具用户友好性的支付类型值,而非直接显示 cc,可在 lang/xx/validation.php 语言文件中通过定义 values 数组来指定:

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

默认情况下,Laravel 应用骨架不包含 lang 目录。若你希望自定义 Laravel 的语言文件,可通过 lang:publish Artisan 命令发布这些文件。

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

当支付类型为 credit card 时,信用卡号字段为必填项。

可用验证规则

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

布尔值

Accepted Accepted If Boolean Declined Declined If

字符串

Active URL Alpha Alpha Dash Alpha Numeric Ascii Confirmed Current Password Different Doesnt Start With Doesnt End With Email Ends With Enum Hex Color In IP Address JSON Lowercase MAC Address Max Min Not In Regular Expression Not Regular Expression Same Size Starts With String Uppercase URL ULID UUID

数字

Between Decimal Different Digits Digits Between Greater Than Greater Than Or Equal Integer Less Than Less Than Or Equal Max Max Digits Min Min Digits Multiple Of Numeric Same Size

数组

Array Array Keys Between Contains Doesnt Contain Distinct In Array In Array Keys List Max Min Size

日期

After After Or Equal Before Before Or Equal Date Date Equals Date Format Different Timezone

文件

Between Dimensions Encoding Extensions File Image Max Min MIME Types MIME Type By File Extension Size

数据库

Exists Unique

实用工具

Any Of Bail Exclude Exclude If Exclude Unless Exclude With Exclude Without Filled Missing Missing If Missing Unless Missing With Missing With All Nullable Present Present If Present Unless Present With Present With All Prohibited Prohibited If Prohibited If Accepted Prohibited If Declined Prohibited Unless Prohibits Required Required If Required If Accepted Required If Declined Required Unless Required With Required With All Required Without Required Without All Required Array Keys Sometimes

规则构建器

除字符串外,许多验证规则也可通过 Illuminate\Validation\Rule 类的方法定义。这些方法提供 IDE 自动补全功能,且可与字符串规则结合使用:

use Illuminate\Validation\Rule;

'name' => [Rule::required(), Rule::string()->max(255)],
'terms' => [Rule::accepted()],
'enabled' => [Rule::boolean()],
'website' => [Rule::nullable(), Rule::activeUrl()],

方法名使用驼峰命名,例如 Rule::hexColor()、Rule::macAddress() 以及 Rule::requiredArrayKeys(['name', 'email'])。依赖其他字段的规则会将字段名或比较值作为参数接收:

'terms' => [Rule::acceptedIf('account_type', ['personal', 'business'])],
'nickname' => [Rule::excludeWith('name')],
'email' => [Rule::requiredWithout(['phone', 'mobile'])],
'settings' => [Rule::presentIf('account_type', 'business')],

acceptedIf、declinedIf、missingIf、missingUnless、presentIf 和 presentUnless 方法接收一个比较值或比较值数组。requiredWith、requiredWithAll、requiredWithout、requiredWithoutAll、missingWith、missingWithAll、presentWith、presentWithAll 和 prohibits 等方法则接收字段名数组或多个字段名参数。

也可通过 regex 和 notRegex 方法定义正则表达式:

'username' => [Rule::required(), Rule::regex('/^[a-z]+$/i')],
'code' => [Rule::notRegex('/^test-/i')],

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 提取该主机名。

在测试执行 DNS 查询的验证规则(如 active_url 和 email:dns)时,可使用 Validator::fakeDnsLookups 方法。该方法会在模拟 DNS 查询的同时,保留规则的其他验证行为:

use Illuminate\Support\Facades\Validator;

Validator::fakeDnsLookups();

after:date

被验证的字段必须是给定日期之后的值。这些日期会被传入 PHP 函数 strtotime,以转换为有效的 DateTime 实例:

'start_date' => ['required', 'date', 'after:tomorrow']

除了传递由 strtotime 解析的日期字符串,还可指定另一个字段与该日期进行比较:

'finish_date' => ['required', 'date', 'after:start_date']

为方便起见,可使用流畅的 date 规则构建器来构建基于日期的规则:

use Illuminate\Validation\Rule;

'start_date' => [
    'required',
    Rule::date()->after(today()->addDays(7)),
],

可使用 afterToday 和 todayOrAfter 方法流畅地表达日期,分别表示必须在今天之后,或今天及之后:

'start_date' => [
    'required',
    Rule::date()->afterToday(),
],

after_or_equal:date

被验证的字段必须是给定日期之后或等于该日期的值。更多信息请参阅 after 规则。

为方便起见,可使用流畅的 date 规则构建器来构建基于日期的规则:

use Illuminate\Validation\Rule;

'start_date' => [
    'required',
    Rule::date()->afterOrEqual(today()->addDays(7)),
],

anyOf

Rule::anyOf 验证规则允许你指定被验证的字段必须满足给定的任意一个验证规则集。例如,以下规则将验证 username 字段要么是电子邮件地址,要么是长度至少为 6 的字母数字字符串(包含连字符):

use Illuminate\Validation\Rule;

'username' => [
    'required',
    Rule::anyOf([
        ['string', 'email'],
        ['string', 'alpha_dash', 'min:6'],
    ]),
],

alpha

被验证的字段必须全部是由 \p{L} 和 \p{M} 中包含的 Unicode 字母字符组成。

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

'username' => ['alpha:ascii'],

alpha_dash

被验证的字段必须全部是由 \p{L}、\p{M}、\p{N} 中包含的 Unicode 字母数字字符,以及 ASCII 连字符(-)和 ASCII 下划线(_)组成。

若要将此验证规则限制为 ASCII 范围内的字符(a-z、A-Z 和 0-9),可向验证规则提供 ascii 选项:

'username' => ['alpha_dash:ascii'],

alpha_num

被验证的字段必须全部包含在\p{L}、\p{M}和\p{N}中的Unicode字母数字字符。

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

'username' => ['alpha_num:ascii'],

array

被验证的字段必须是 PHP array。

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

use Illuminate\Support\Facades\Validator;

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

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

通常情况下,您应始终指定允许存在于数组中的数组键。

array_keys:foo,bar,...

被验证的字段必须是一个 PHP array,且其所有键都包含在给定列表中。至少需要提供一个键:

'user' => ['array_keys:name,username'],

为方便起见,您可以使用 Rule::arrayKeys 方法:

'user' => [Rule::arrayKeys('name', 'username')],

ascii

被验证的字段必须全部为7位ASCII字符。

bail

在首次验证失败后,停止对该字段运行验证规则。

虽然 bail 规则仅会在遇到验证失败时停止验证特定字段,但 stopOnFirstFailure 方法会告知验证器,一旦发生单次验证失败,就应停止对所有属性的验证:

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

before:date

被验证的字段必须是早于给定日期的值。这些日期会被传入 PHP strtotime 函数,以便转换为有效的 DateTime 实例。此外,与 after 规则类似,您可以将另一个被验证字段的名称作为 date 的值提供。

为方便起见,也可以使用流畅的 date 规则构建器来构建基于日期的规则:

use Illuminate\Validation\Rule;

'start_date' => [
    'required',
    Rule::date()->before(today()->subDays(7)),
],

可以使用 beforeToday 和 todayOrBefore 方法来流畅地表达日期必须早于今天,或者为今天或早于今天:

'start_date' => [
    'required',
    Rule::date()->beforeToday(),
],

before_or_equal:date

被验证的字段必须是早于或等于给定日期的值。这些日期会被传入 PHP strtotime 函数,以便转换为有效的 DateTime 实例。此外,与 after 规则类似,您可以将另一个被验证字段的名称作为 date 的值提供。

为方便起见,也可以使用流畅的 date 规则构建器来构建基于日期的规则:

use Illuminate\Validation\Rule;

'start_date' => [
    'required',
    Rule::date()->beforeOrEqual(today()->subDays(7)),
],

between:min,max

被验证的字段的大小必须在给定的 min 和 max 之间(包含边界值)。字符串、数值、数组和文件的评估方式与 size 规则相同。

boolean

被验证的字段必须能够转换为布尔值。接受的输入有 true、false、1、0、"1" 和 "0"。

您可以使用 strict 参数,仅当字段值为 true 或 false 时才视为有效:

'foo' => ['boolean:strict']

或者,您可以使用 Rule::boolean 方法,传入 true 以启用严格验证:

'foo' => [Rule::boolean(true)],

confirmed

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

您还可以传入自定义的确认字段名称。例如,confirmed:repeat_username 会期望 repeat_username 字段与被验证字段匹配。

您也可以使用 Rule::confirmed 方法,通过 customField 方法指定自定义确认字段:

'username' => [Rule::confirmed()->customField('repeat_username')],

contains:foo,bar,...

被验证的字段必须是包含所有给定参数值的数组。由于该规则通常需要您对数组进行 implode 操作,因此可以使用 Rule::contains 方法来流畅地构建该规则:

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

Validator::make($data, [
    'roles' => [
        'required',
        'array',
        Rule::contains(['admin', 'editor']),
    ],
]);

doesnt_contain:foo,bar,...

被验证的字段必须是不包含任何给定参数值的数组。由于该规则通常需要您对数组进行 implode 操作,因此可以使用 Rule::doesntContain 方法来流畅地构建该规则:

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

Validator::make($data, [
    'roles' => [
        'required',
        'array',
        Rule::doesntContain(['admin', 'editor']),
    ],
]);

current_password

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

'password' => ['current_password:api']

或者,您可以使用 Rule::currentPassword 方法,并通过 guard 方法指定认证守卫:

'password' => [Rule::currentPassword()->guard('api')],

date

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

date_equals:date

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

为方便起见,可以使用流畅的 date 规则构建器来构建基于日期的规则:

use Illuminate\Validation\Rule;

'start_date' => [Rule::date()->equals('2026-01-01')],

equals 方法接受日期字符串、DateTimeInterface 实例或要比较的另一个字段的名称。

date_format:format,...

被验证的字段必须匹配给定的任一 formats。验证字段时,您应只使用 date 或 date_format 其中之一,不能同时使用两者。该验证规则支持 PHP 的 DateTime 类所支持的所有格式。

为方便起见,可以使用流畅的 date 规则构建器来构建基于日期的规则:

use Illuminate\Validation\Rule;

'start_date' => [
    'required',
    Rule::date()->format('Y-m-d'),
],

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

被验证的字段的值必须与 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、min_ratio、max_ratio。

ratio 约束应以宽除以高的形式表示。这可以通过类似 3/2 的分数或类似 1.5 的浮点数来指定:

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

可以使用 min_ratio 和 max_ratio 约束来定义可接受的宽高比范围:

'avatar' => ['dimensions:min_ratio=1/2,max_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),
    ],
]);

还可以使用 minRatio、maxRatio 和 ratioBetween 方法以流畅方式定义比例约束:

Rule::dimensions()->ratioBetween(min: 1 / 2, max: 3 / 2);

distinct

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

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

默认情况下,distinct 使用松散变量比较。若要使用严格比较,可在验证规则定义中添加 strict 参数:

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

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

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

或者,可以使用 Rule::distinct 方法,按需链式调用 strict 和 ignoreCase 方法:

'foo.*.id' => [Rule::distinct()->strict()->ignoreCase()],

doesnt_start_with:foo,bar,...

待验证字段不得以给定值中的任意一个开头。

doesnt_end_with:foo,bar,...

待验证字段不得以给定值中的任意一个结尾。

email

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

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

上面的示例将应用 RFCValidation 和 DNSCheckValidation 验证。以下是可应用的验证方式完整列表:

  • rfc:RFCValidation - 根据支持的 RFC 标准验证电子邮件地址。
  • strict:NoRFCWarningsValidation - 根据支持的 RFC 标准验证电子邮件,若发现警告(如末尾的句点和多个连续句点)则验证失败。
  • dns:DNSCheckValidation - 确保电子邮件地址的域名具有有效的 MX 记录。
  • spoof:SpoofCheckValidation - 确保电子邮件地址不包含同形字或具有欺骗性的 Unicode 字符。
  • filter:FilterEmailValidation - 根据 PHP 的 filter_var 函数确保电子邮件地址有效。
  • filter_unicode:FilterEmailValidation::unicode() - 根据 PHP 的 filter_var 函数确保电子邮件地址有效,同时允许部分 Unicode 字符。

为方便起见,可以使用流畅的规则构建器来构建电子邮件验证规则:

use Illuminate\Validation\Rule;

$request->validate([
    'email' => [
        'required',
        Rule::email()
            ->rfcCompliant(strict: false)
            ->validateMxRecord()
            ->preventSpoofing()
    ],
]);

dns 验证器会执行实际的 DNS 查询,以确认地址的域名具有有效的 MX 记录。它不会判定单个邮箱是否存在。

由于测试不应依赖实时 DNS 查询,可以使用 Validator::fakeDnsLookups 方法伪造 DNS 查询,同时其他请求的验证(如 rfc)仍会继续运行:

use Illuminate\Support\Facades\Validator;

Validator::fakeDnsLookups();

这使得你的应用程序在测试时可以继续使用现有的验证规则:

'email' => ['required', 'email:rfc,dns'],

dns 和 spoof 验证器需要 PHP 的 intl 扩展。

encoding:encoding_type

待验证字段必须匹配指定的字符编码。该规则使用 PHP 的 mb_check_encoding 函数来验证给定文件或字符串值的编码。为方便起见,可以使用 Laravel 的流畅文件规则构建器来构建 encoding 规则:

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

Validator::validate($input, [
    'attachment' => [
        'required',
        File::types(['csv'])
            ->encoding('utf-8'),
    ],
]);

ends_with:foo,bar,...

待验证字段必须以给定值中的任意一个结尾。

enum

Enum 规则是一种基于类的规则,用于验证待验证字段是否包含有效的枚举值。Enum 规则仅接受枚举名称作为其构造函数参数。验证原始值时,应将有值的枚举提供给 Enum 规则:

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

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

可以使用 Enum 规则的 only 和 except 方法来限定哪些枚举情况应被视为有效:

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

待验证字段将从 validate 和 validated 方法返回的请求数据中排除。

exclude_if:anotherfield,value

若 anotherfield 字段等于 value,则待验证字段将从 validate 和 validated 方法返回的请求数据中排除。

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

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,否则待验证字段将从 validate 和 validated 方法返回的请求数据中排除。如果 value 为 null(exclude_unless:name,null),除非比较字段为 null 或比较字段在请求数据中缺失,否则待验证字段将被排除。

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

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

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

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

exclude_with:anotherfield

如果存在 anotherfield 字段,则被验证的字段将从 validate 和 validated 方法返回的请求数据中排除。

exclude_without:anotherfield

如果 anotherfield 字段不存在,则被验证的字段将从 validate 和 validated 方法返回的请求数据中排除。

exists:table,column

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

exists 规则的基本用法

'state' => ['exists:states']

如果未指定 column 选项,则会使用字段名。因此,在此情况下,该规则将验证 states 数据库表中是否存在一条记录,其 state 列的值与请求中的 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 方法生成的 exists 规则应使用的数据库列名:

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

有时,您可能希望验证一组值是否存在于数据库中。您可以通过为正在验证的字段添加 exists 和数组规则来实现这一点:

'states' => ['array', Rule::exists('states', 'abbreviation')],

当这两个规则都分配给一个字段时,Laravel 将自动构建一个查询,以确定所有给定的值是否存在于指定的表中。

extensions:foo,bar,...

被验证的文件必须具有与列出的扩展名之一对应的用户指定的扩展名:

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

您绝不应仅依赖用户指定的扩展名来验证文件。此规则通常应始终与 mimes 或 mimetypes 规则结合使用。

file

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

filled

当字段存在时,被验证的字段不得为空。

gt:field

被验证的字段必须大于给定的 field 或 value。这两个字段必须为同一类型。字符串、数值、数组和文件将使用与 size 规则相同的约定进行评估。

gte:field

被验证的字段必须大于或等于给定的 field 或 value。这两个字段必须为同一类型。字符串、数值、数组和文件将使用与 size 规则相同的约定进行评估。

hex_color

被验证的字段必须包含 十六进制格式的有效颜色值。

image

被验证的文件必须为图像(jpg、jpeg、png、bmp、gif、webp、avif、heic 或 heif)。

默认情况下,由于可能存在 XSS 漏洞,image 规则不允许 SVG 文件。如果您需要允许 SVG 文件,可以向 image 规则提供 allow_svg 指令(image:allow_svg)。

in:foo,bar,...

被验证的字段必须包含在给定的值列表中。由于该规则通常需要您对数组执行 implode 操作,因此可以使用 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 的值中。

in_array_keys:value.*

被验证的字段必须是数组,并且该数组中至少有一个给定的 values 作为键:

'config' => ['array', 'in_array_keys:timezone']

integer

被验证的字段必须是整数。

您可以使用 strict 参数,仅当字段的类型为 integer 时才将其视为有效。具有整数值的字符串将被视为无效:

'age' => ['integer:strict']

此验证规则不会验证输入是否为“integer”变量类型,只会验证输入是否为 PHP 的 FILTER_VALIDATE_INT 规则所接受的类型。如果您需要将输入验证为数字,请将此规则与 numeric 验证规则结合使用。

ip

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

您也可以使用 Rule::ip 方法,可选择将 IP 版本限制为 4 或 6:

'ip_address' => [Rule::ip()],
'ipv4_address' => [Rule::ip()->version(4)],
'ipv6_address' => [Rule::ip()->version(6)],

ipv4

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

ipv6

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

json

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

lt:field

被验证的字段必须小于给定的 field。这两个字段必须为同一类型。字符串、数值、数组和文件将使用与 size 规则相同的约定进行评估。

lte:field

被验证的字段必须小于或等于给定的 field。这两个字段必须为同一类型。字符串、数值、数组和文件将使用与 size 规则相同的约定进行评估。

lowercase

被验证的字段必须为小写。

list

被验证的字段必须是一个列表类型的数组。如果数组的键是从 0 到 count($array) - 1 的连续数字,则该数组被视为列表。

mac_address

被验证的字段必须是 MAC 地址。

max:value

被验证的字段必须小于或等于最大 value。字符串、数值、数组和文件将按照与 size 规则相同的方式进行评估。

max_digits:value

被验证的整数的长度不得超过 value。

mimetypes:text/plain,...

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

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

'media' => ['mimetypes:image/*,video/*'],

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

mimes:foo,bar,...

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

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

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

https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types

MIME 类型与扩展名

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

min:value

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

min_digits:value

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

multiple_of:value

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

missing

被验证的字段不得出现在输入数据中。

missing_if:anotherfield,value,...

如果 anotherfield 字段等于任何 value,则被验证的字段不得出现。

missing_unless:anotherfield,value

除非 anotherfield 字段等于任何 value,否则被验证的字段不得出现。

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']。

nullable

被验证的字段可以为 null。

numeric

被验证的字段必须是数字。

您可以使用 strict 参数,只有当字段的值是整数或浮点类型时才认为该字段有效。数字字符串将被视为无效:

'amount' => ['numeric:strict']

present

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

present_if:anotherfield,value,...

如果 anotherfield 字段等于任何 value,则被验证的字段必须存在。

present_unless:anotherfield,value

除非 anotherfield 字段等于任何 value,否则被验证的字段必须存在。

present_with:foo,bar,...

仅当任何其他指定字段存在时,被验证的字段才必须存在。

present_with_all:foo,bar,...

仅当所有其他指定字段都存在时,被验证的字段才必须存在。

prohibited

被验证的字段必须缺失或为空。如果字段满足以下任一条件,则视为“空”:

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

prohibited_if:anotherfield,value,...

如果 anotherfield 字段等于任何 value,则被验证的字段必须缺失或为空。如果字段满足以下任一条件,则视为“空”:

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

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

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_if_accepted:anotherfield,...

如果 anotherfield 字段等于 "yes"、"on"、1、"1"、true 或 "true",则被验证的字段必须缺失或为空。

prohibited_if_declined:anotherfield,...

如果 anotherfield 字段等于 "no"、"off"、0、"0"、false 或 "false",则被验证的字段必须缺失或为空。

prohibited_unless:anotherfield,value,...

除非 anotherfield 字段等于任何 value,否则被验证的字段必须缺失或为空。如果字段满足以下任一条件,则视为“空”:

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

如果需要复杂的条件禁止逻辑,可以使用 Rule::prohibitedUnless 方法。该方法接受布尔值或闭包。当传入闭包时,该闭包应返回 true 或 false,以指示是否不应禁止被验证的字段:

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

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

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

prohibits:anotherfield,...

如果被验证的字段未缺失或为空,则 anotherfield 中的所有字段必须缺失或为空。如果字段满足以下任一条件,则视为“空”:

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

regex:pattern

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

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

required

被验证的字段必须存在于输入数据中且不为空。如果字段满足以下任一条件,则视为“空”:

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

required_if:anotherfield,value,...

如果 anotherfield 字段等于任何 value,则被验证的字段必须存在且不为空。

如果您希望为 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 字段等于任意一个 value,否则被验证的字段必须存在且不能为空。这同时意味着,除非 value 为 null,否则 anotherfield 必须存在于请求数据中。如果 value 为 null(required_unless:name,null),则除非对比字段为 null,或者对比字段在请求数据中不存在,否则被验证的字段为必填项。

如果想为 required_unless 规则构建更复杂的条件,可以使用 Rule::requiredUnless 方法。该方法接受布尔值或闭包。传入闭包时,闭包应返回 true 或 false,用以表明被验证字段是否非必填:

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

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

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

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

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

size:value

被验证的字段大小必须与给定的 value 一致。对于字符串数据,value 对应字符数量。对于数值数据,value 对应给定的整数值(该属性还必须包含 numeric 或 integer 规则)。对于数组,size 对应数组的 count。对于文件,size 对应以千字节为单位的文件大小。来看一些示例:

// Validate that a string is exactly 12 characters long...
'title' => ['size:12'];

// Validate that a provided integer equals 10...
'seats' => ['integer', 'size:10'];

// Validate that an array has exactly 5 elements...
'tags' => ['array', 'size:5'];

// Validate that an uploaded file is exactly 512 kilobytes...
'image' => ['file', 'size:512'];

starts_with:foo,bar,...

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

string

被验证的字段必须是字符串。如果想允许该字段也可以为 null,应当为该字段赋予 nullable 规则。

为方便起见,也可以使用流式的 Rule::string() 规则构建器来构建字符串验证规则:

use Illuminate\Validation\Rule;

'title' => [
    'required',
    Rule::string()
        ->min(3)
        ->max(255)
        ->alphaDash(ascii: true),
],

字符串规则构建器提供了常用字符串约束的方法,包括 alpha、alphaDash、alphaNumeric、ascii、between、doesntEndWith、doesntStartWith、endsWith、exactly、lowercase、max、min、startsWith 以及 uppercase。由于规则构建器支持条件判断,还可以使用 when 和 unless 方法按需应用约束。

timezone

被验证的字段必须是 DateTimeZone::listIdentifiers 方法认可的有效时区标识符。

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

'timezone' => ['required', 'timezone:all'];

'timezone' => ['required', 'timezone:Africa'];

'timezone' => ['required', 'timezone:per_country,US'];

此外,也可以使用 Rule::timezone 方法,并将任意 listIdentifiers 参数作为数组传入:

'timezone' => [Rule::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。例如,考虑一个包含用户名、邮箱地址和位置信息的“更新资料”页面,你可能需要验证邮箱地址具有唯一性。然而,如果用户只修改了 name 字段而没有修改 email 字段,则不希望抛出验证错误,因为该用户本来就是所涉及邮箱地址的所有者。

为了指示验证器忽略用户 ID,我们可以使用 Rule 类以流式方式定义该规则。

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

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

切勿将任何由用户控制的请求输入传入 ignore 方法。你应该只传入系统生成的唯一 ID,例如来自 Eloquent 模型实例的自增 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_id 列值为 1 的记录:

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

在唯一性检查中忽略软删除记录:

默认情况下,unique 规则在判定唯一性时会包含软删除记录。要从唯一性检查中排除软删除记录,可以调用 withoutTrashed 方法:

Rule::unique('users')->withoutTrashed();

如果模型使用 deleted_at 以外的列名来标记软删除记录,可以在调用 withoutTrashed 方法时提供该列名:

Rule::unique('users')->withoutTrashed('was_deleted_at');

uppercase

被验证的字段必须是大写形式。

url

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

如果你希望指定应被视为有效的URL协议,你可以将这些协议作为校验规则参数传入:

'url' => ['url:http,https'],

'game' => ['url:minecraft,steam'],

或者,你可以使用 Rule::url 方法,并通过 protocols 方法指定允许的协议:

'url' => [Rule::url()->protocols(['http', 'https'])],

ulid

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

uuid

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

你还可以校验给定的UUID是否匹配指定版本的UUID规范:

'uuid' => ['uuid:4']

或者,你可以使用 Rule::uuid 方法,并通过 version 方法指定UUID版本:

'uuid' => [Rule::uuid()->version(4)],

条件性添加规则

当字段具有特定值时跳过校验

有时你可能希望在另一个字段具有特定值时,不对给定字段执行校验。你可以使用 exclude_if 校验规则来实现这一点。在此示例中,如果 has_appointment 字段的值为 false,则 appointment_date 和 doctor_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', 'integer', 'min:0'],
]);

假设我们的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' => '[email protected]',
        ],
        [
            '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'],
]);

通常,你应该始终指定数组中允许存在的键。否则,校验器的 validate 和 validated 方法将返回所有经过校验的数据,包括数组及其所有键,即便这些键并未被其他嵌套数组校验规则校验过。

校验嵌套数组输入

校验基于嵌套数组的表单输入字段不必是一件麻烦事。你可以使用“点语法”来校验数组内的属性。例如,如果传入的HTTP请求包含 photos[profile] 字段,你可以按如下方式进行校验:

use Illuminate\Support\Facades\Validator;

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

你也可以对数组中的每个元素进行校验。例如,要校验给定数组输入字段中的每个电子邮件都是唯一的,你可以执行以下操作:

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

同样,在语言文件中指定自定义校验消息时,你可以使用 * 字符,从而轻松地对基于数组的字段使用单一的校验消息:

'custom' => [
    'users.*.email' => [
        'unique' => 'Each user must have a unique email address',
    ]
],

访问嵌套数组数据

在为属性分配校验规则时,有时你可能需要访问给定嵌套数组元素的值。你可以使用 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 开始)或 :ordinal-position(从 1st 开始)占位符:

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."

如有需要,你可以通过 second-index、second-position、third-index、third-position 等方式引用更深层嵌套的索引和位置。

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

验证文件

Laravel 提供了多种可用于验证上传文件的验证规则,例如 mimes、image、min 和 max。虽然你在验证文件时可以自由单独指定这些规则,但 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),
    ],
]);

验证文件类型

尽管调用 types 方法时只需指定扩展名,但该方法实际上会通过读取文件内容并推测其 MIME 类型来验证文件的 MIME 类型。完整的 MIME 类型及其对应扩展名列表可在以下位置找到:

https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types

验证文件大小

为方便起见,可通过带有表示文件大小单位的后缀的字符串来指定文件的最小和最大大小。支持 kb、mb、gb 和 tb 后缀:

File::types(['mp3', 'wav'])
    ->min('1kb')
    ->max('10mb');

验证图像文件

如果你的应用程序接受用户上传的图像,可以使用 File 规则的 image 构造方法,确保被验证的文件是图像(jpg、jpeg、png、bmp、gif、webp、avif、heic 或 heif)。

此外,可以使用 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)),
    ],
]);

有关验证图像尺寸的更多信息,可在 dimension 规则文档中找到。

默认情况下,由于可能存在 XSS 漏洞,image 规则不允许 SVG 文件。如果需要允许 SVG 文件,可以向 image 规则传递 allowSvg: true:File::image(allowSvg: true)。

验证图像尺寸

你也可以验证图像的尺寸。例如,要验证上传的图像至少宽 1000 像素、高 500 像素,可以使用 dimensions 规则:

use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\File;

File::image()->dimensions(
    Rule::dimensions()
        ->maxWidth(1000)
        ->maxHeight(500)
);

有关验证图像尺寸的更多信息,可在 dimension 规则文档中找到。

验证密码

为确保密码具备足够的复杂度,可以使用 Laravel 的 Password 规则对象:

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

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

Password 规则对象让你可以轻松自定义应用程序的密码复杂度要求,例如指定密码至少需要一个字母、数字、符号,或大小写混合的字符:

// Require at least 8 characters...
Password::min(8);

// Require at most 256 characters...
Password::min(16)->max(256);

// Require at least one letter...
Password::min(8)->letters();

// Require at least one uppercase and one lowercase letter...
Password::min(8)->mixedCase();

// Require at least one number...
Password::min(8)->numbers();

// Require at least one symbol...
Password::min(8)->symbols();

此外,你可以使用 uncompromised 方法确保密码未在公开的密码数据泄露事件中被泄露:

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

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

默认情况下,如果密码在数据泄露中至少出现一次,就会被判定为已泄露。你可以通过 uncompromised 方法的第一个参数自定义此阈值:

// Ensure the password appears no more than 3 times in the same data leak...
Password::min(8)->uncompromised(3);

当然,你可以将上述示例中的所有方法进行链式调用:

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

你可以使用 toPasswordRulesString 方法,将 Password 规则对象转换为适合 HTML passwordrules 属性的字符串:

<input
    type="password"
    name="password"
    autocomplete="new-password"
    passwordrules="{{ Password::defaults()->toPasswordRulesString() }}"
/>

定义默认密码规则

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

use Illuminate\Validation\Rules\Password;

/**
 * Bootstrap any application services.
 */
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 目录下。如果该目录不存在,在你执行用于创建规则的 Artisan 命令时,Laravel 会自动创建它:

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.");
            }
        },
    ],
]);

隐式规则

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

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

“隐式”规则仅隐含该属性为必填项。是否实际判定缺失或空属性无效,由你决定。