验证

简介

Laravel 提供了强大的数据验证功能。最常见的做法是使用表单请求(Form Request)——一种自定义请求类,封装了自己的验证和授权逻辑。验证失败时,Laravel 会自动返回包含错误信息的 JSON 响应。

Laravel 自带了大量的便捷验证规则,你可以直接应用到数据上,甚至能验证某个值在数据库表中是否唯一。我们会逐一介绍这些验证规则,让你熟悉 Laravel 的全部验证功能。

验证快速入门

为了了解 Laravel 强大的验证功能,我们来看一个完整的例子——通过表单请求验证 API 收到的数据。

创建表单请求

用 make:request Artisan 命令创建表单请求类:

php artisan make:request StorePostRequest

生成的表单请求类会放在 app/Http/Requests 目录下。如果这个目录不存在,运行 make:request 命令时会自动创建。Laravel 生成的每个表单请求都有两个方法:authorize() 和 rules()。

authorize() 方法负责判断当前认证用户是否有权限执行该请求代表的操作,rules() 方法返回应用到请求数据的验证规则:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    /**
     * 判断用户是否有权发起此请求。
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * 获取应用到请求的验证规则。
     *
     * @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 服务容器自动解析。

验证规则怎么执行呢?只需要在控制器方法中类型提示该请求。来访的表单请求会在控制器方法调用前完成验证,所以你不用在控制器里写任何验证逻辑:

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StorePostRequest;
use Illuminate\Http\JsonResponse;

class PostController extends Controller
{
    /**
     * 存储新博客文章。
     */
    public function store(StorePostRequest $request): JsonResponse
    {
        // 来访请求已验证通过...

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

        // 存储博客文章...

        return response()->json(['message' => 'Post created successfully.'], 201);
    }
}

如果验证失败,Laravel 会自动返回 422 Unprocessable Entity HTTP 响应,包含验证错误的 JSON 表示。

有时候你可能希望某个字段在第一次验证失败后就停止运行后续规则。给该字段加上 bail 规则即可:

public function rules(): array
{
    return [
        'title' => ['bail', 'required', 'unique:posts', 'max:255'],
        'body' => ['required'],
    ];
}

这个例子中,如果 title 字段的 unique 规则验证失败,max 规则就不会再检查了。规则按定义的顺序执行。

信息

需要为前端添加实时表单请求验证?看看 Laravel Precognition。

关于嵌套属性的说明

如果来访请求包含"嵌套"字段数据,可以在验证规则中用"点号"语法指定这些字段:

public function rules(): array
{
    return [
        'title' => ['required', 'unique:posts', 'max:255'],
        'author.name' => ['required'],
        'author.description' => ['required'],
    ];
}

如果字段名本身包含点号,可以用反斜杠转义,防止被解释为"点号"语法:

public function rules(): array
{
    return [
        'title' => ['required', 'unique:posts', 'max:255'],
        'v1\.0' => ['required'],
    ];
}

验证错误响应格式

当验证失败时,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."
        ]
    }
}

关于可选字段的说明

默认情况下,Laravel 在全局中间件中包含了 TrimStrings 和 ConvertEmptyStringsToNull 中间件。因此,如果你不想让验证器把 null 值视为无效,通常需要把"可选"请求字段标记为 nullable。比如:

public function rules(): array
{
    return [
        'title' => ['required', 'unique:posts', 'max:255'],
        'body' => ['required'],
        'publish_at' => ['nullable', 'date'],
    ];
}

这个例子中,publish_at 字段可以是 null 或一个有效的日期。如果不加 nullable,验证器会把 null 视为无效日期。

表单请求验证

授权表单请求

表单请求类还包含一个 authorize() 方法。在这个方法中,你可以判断认证用户是否真的有权更新某个资源。比如判断用户是否真的拥有他试图更新的博客评论。你通常会在这个方法中与授权门面和策略交互:

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

因此,如果你的应用用了路由模型绑定,代码可以更简洁——直接把解析出的模型作为请求属性访问:

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' => 'A title is required',
        'body.required' => 'A message is required',
    ];
}

可以用"点号"语法只为特定属性指定消息。先指定属性名,再跟上规则:

public function messages(): array
{
    return [
        'email.required' => 'We need to know your email address!',
    ];
}

验证消息中还可以使用占位符。例如,:attribute 占位符会被替换为被验证字段的实际名称:

public function messages(): array
{
    return [
        'required' => 'The :attribute field is required.',
        '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',
    ];
}

自定义验证属性名

Laravel 很多内置验证规则的错误消息包含 :attribute 占位符。如果你想让验证消息中的 :attribute 占位符替换为自定义的属性名,可以覆盖 attributes() 方法。该方法应返回一个属性/名称对的数组:

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

执行额外验证

有时候你需要在初始验证完成后再执行一些额外验证。可以用表单请求的 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',
                    'Something is wrong with this 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) {
            //
        }
    ];
}

什么场景用after()?

规则能搞定的用规则,规则搞不定的用 after()。 比如:跨字段业务规则、需要查数据库的复合验证、基于已验证的数据做进一步检查、调用外部服务等。已有的规则并不能覆盖所有的业务验证场景,当已有的规则不能解决验证的时候,就可以使用after()来写自定义的验证逻辑。

遇到第一个验证失败就停止

给请求类加上 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;

/**
 * 启动应用服务。
 */
public function boot(): void
{
    FormRequest::failOnUnknownFields();
}

如果需要,可以给属性传 false 来为特定请求禁用这个行为:

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

拒绝未知字段可以提供额外的防护,防止意外的输入键深入应用,避免批量赋值类问题。但你仍应配置模型的 $fillable / $guarded 属性,只持久化可信的、验证过的输入。

自定义错误包

表单请求验证失败时,错误默认存到 default 错误包。如果需要把错误存到不同的错误包中,可以在表单请求上使用 ErrorBag 属性:

<?php

namespace App\Http\Requests;

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

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

为验证准备输入

如果你需要在应用验证规则之前准备或清理请求数据,可以用 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']);
}

使用验证后的输入

使用表单请求验证来访请求数据后,你可能想获取实际经过验证的请求数据。有几种方式可以实现。首先,可以在表单请求上调用 validated 方法。该方法返回验证后的数据数组:

$validated = $request->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 实例可以像数组一样遍历和访问:

// 验证后的数据可以遍历...
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 实例,它提供了多种便捷方法来处理错误消息。这在表单请求的 after() 回调中或自定义错误处理时特别有用。

获取字段的第一个错误消息

要获取某个字段的第一个错误消息,使用 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 时,信用卡号为必填:

public function rules(): array
{
    return [
        '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 · 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 · 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

accepted

验证字段必须是 "yes"、"on"、1、"1"、true 或 "true"。这适用于验证"服务条款"接受或类似字段。

accepted_if:anotherfield,value,...

如果另一个验证字段等于指定值,则验证字段必须是 "yes"、"on"、1、"1"、true 或 "true"。这适用于验证"服务条款"接受或类似字段。

active_url

验证字段必须根据 dns_get_record PHP 函数具有有效的 A 或 AAAA 记录。提供的 URL 的主机名会先用 parse_url PHP 函数提取,再传给 dns_get_record。

after:date

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

public function rules(): array
{
    return [
        'start_date' => ['required', 'date', 'after:tomorrow'],
    ];
}

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

public function rules(): array
{
    return [
        'finish_date' => ['required', 'date', 'after:start_date'],
    ];
}

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

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        '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 规则提供的值列表中:

public function rules(): array
{
    return [
        'user' => ['array:name,username'],
    ];
}

通常,你应该始终指定数组中允许存在的键。

ascii

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

bail

在第一次验证失败后停止该字段的后续验证规则。

bail 规则只在遇到验证失败时停止验证特定字段,而 StopOnFirstFailure 属性会告诉验证器一旦出现一个验证失败就停止验证所有属性。

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

confirmed

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

你也可以传入自定义确认字段名。例如,confirmed:repeat_username 会要求 repeat_username 字段与验证字段匹配。

contains:foo,bar,...

验证字段必须是包含所有给定参数值的数组。由于此规则通常需要你 implode 一个数组,可以使用 Rule::contains 方法来流式构造规则:

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        'roles' => [
            'required',
            'array',
            Rule::contains(['admin', 'editor']),
        ],
    ];
}

doesnt_contain:foo,bar,...

验证字段必须是不包含任何给定参数值的数组。由于此规则通常需要你 implode 一个数组,可以使用 Rule::doesntContain 方法来流式构造规则:

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        'roles' => [
            'required',
            'array',
            Rule::doesntContain(['admin', 'editor']),
        ],
    ];
}

current_password

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

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

date

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

date_equals:date

验证字段必须等于给定日期。日期会传入 PHP strtotime 函数以转换为有效的 DateTime 实例。

date_format:format,...

验证字段必须匹配给定的 format 之一。验证字段时应该使用 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\Validation\Rule;

public function rules(): array
{
    return [
        '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']

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;

public function rules(): array
{
    return [
        'email' => [
            'required',
            Rule::email()
                ->rfcCompliant(strict: false)
                ->validateMxRecord()
                ->preventSpoofing()
        ],
    ];
}

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

encoding:encoding_type

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

use Illuminate\Validation\Rules\File;

public function rules(): array
{
    return [
        'attachment' => [
            'required',
            File::types(['csv'])
                ->encoding('utf-8'),
        ],
    ];
}

ends_with:foo,bar,...

验证字段必须以给定的某个值结尾。

enum

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

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

public function rules(): array
{
    return [
        '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\Validation\Rule;

public function rules(): array
{
    return [
        'role_id' => [Rule::excludeIf($this->user()->is_admin)],
    ];
}

// 或使用闭包
public function rules(): array
{
    return [
        'role_id' => [Rule::excludeIf(fn () => $this->user()->is_admin)],
    ];
}

exclude_unless:anotherfield,value

除非 anotherfield 字段等于 value,否则验证字段将从 validate 和 validated 方法返回的请求数据中排除。如果 value 为 null(exclude_unless:name,null),除非比较字段为 null 或请求数据中缺少比较字段,否则验证字段将被排除。

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

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        'role_id' => [Rule::excludeUnless($this->user()->is_admin)],
    ];
}

// 或使用闭包
public function rules(): array
{
    return [
        'role_id' => [Rule::excludeUnless(fn () => $this->user()->is_admin)],
    ];
}

exclude_with:anotherfield

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

exclude_without:anotherfield

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

exists:table,column

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

Exists 规则的基本用法 {#basic-usage-of-exists-rule}

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

如果未指定 column 选项,将使用字段名。因此,在这种情况下,规则会验证 states 数据库表中是否存在 state 列值与请求的 state 属性值匹配的记录。

指定自定义列名 {#specifying-a-custom-column-name}

可以通过在数据库表名后放置列名来显式指定验证规则应使用的数据库列名:

'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\Validation\Rule;

public function rules(): array
{
    return [
        'email' => [
            'required',
            Rule::exists('staff')->where(function (Builder $query) {
                $query->where('account_id', 1);
            }),
        ],
    ];
}

可以通过向 exists 方法提供列名作为第二个参数来显式指定 Rule::exists 方法生成的 exists 规则应使用的数据库列名:

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

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

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

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

in:foo,bar,...

验证字段必须包含在给定的值列表中。由于此规则通常需要你 implode 一个数组,可以使用 Rule::in 方法来流式构造规则:

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        'zones' => [
            'required',
            Rule::in(['first-zone', 'second-zone']),
        ],
    ];
}

当 in 规则与 array 规则组合使用时,输入数组中的每个值都必须存在于 in 规则提供的值列表中。在以下示例中,输入数组中的 LAS 机场代码无效,因为它不包含在 in 规则提供的机场列表中:

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        '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 地址。

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-types-and-extensions}

此验证规则不验证 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;

public function rules(): array
{
    return [
        '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\Validation\Rule;

public function rules(): array
{
    return [
        'role_id' => [Rule::prohibitedIf($this->user()->is_admin)],
    ];
}

// 或使用闭包
public function rules(): array
{
    return [
        'role_id' => [Rule::prohibitedIf(fn () => $this->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\Validation\Rule;

public function rules(): array
{
    return [
        'role_id' => [Rule::prohibitedUnless($this->user()->is_admin)],
    ];
}

// 或使用闭包
public function rules(): array
{
    return [
        'role_id' => [Rule::prohibitedUnless(fn () => $this->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\Validation\Rule;

public function rules(): array
{
    return [
        'role_id' => [Rule::requiredIf($this->user()->is_admin)],
    ];
}

// 或使用闭包
public function rules(): array
{
    return [
        'role_id' => [Rule::requiredIf(fn () => $this->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\Validation\Rule;

public function rules(): array
{
    return [
        'role_id' => [Rule::requiredUnless($this->user()->is_admin)],
    ];
}

// 或使用闭包
public function rules(): array
{
    return [
        'role_id' => [Rule::requiredUnless(fn () => $this->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 对应文件大小(千字节)。来看一些示例:

// 验证字符串恰好 12 个字符长...
'title' => ['size:12'];

// 验证提供的整数等于 10...
'seats' => ['integer', 'size:10'];

// 验证数组恰好有 5 个元素...
'tags' => ['array', 'size:5'];

// 验证上传文件恰好 512 千字节...
'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'];

unique:table,column

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

指定自定义表/列名:

除了直接指定表名外,还可以指定 Eloquent 模型来确定表名:

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

column 选项可用于指定字段对应的数据库列。如果未指定 column 选项,将使用验证字段的名称。

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

指定自定义数据库连接

有时,你可能需要为验证器执行的数据库查询设置自定义连接。可以在表名前加上连接名来实现:

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

强制 Unique 规则忽略给定 ID:

有时,你可能希望在唯一验证中忽略给定的 ID。例如,考虑一个"更新资料"场景,包含用户名、电子邮件地址和位置。你可能想验证电子邮件地址是否唯一。但是,如果用户只更改了名字字段而没有更改电子邮件字段,你不希望因为用户已经是该电子邮件地址的所有者而抛出验证错误。

要指示验证器忽略用户 ID,我们使用 Rule 类来流式定义规则。

use Illuminate\Validation\Rule;

public function rules(): array
{
    return [
        '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'],

ulid

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

uuid

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

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

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

条件性地添加规则

字段有特定值时跳过验证

有时候你可能希望在另一个字段有特定值时不验证某个字段。可以用 exclude_if 验证规则实现。这个例子中,如果 has_appointment 字段的值为 false,appointment_date 和 doctor_name 字段将不被验证:

public function rules(): array
{
    return [
        'has_appointment' => ['required', 'boolean'],
        'appointment_date' => ['exclude_if:has_appointment,false', 'required', 'date'],
        'doctor_name' => ['exclude_if:has_appointment,false', 'required', 'string'],
    ];
}

也可以用 exclude_unless 规则在另一个字段没有特定值时不验证某个字段:

public function rules(): array
{
    return [
        'has_appointment' => ['required', 'boolean'],
        'appointment_date' => ['exclude_unless:has_appointment,true', 'required', 'date'],
        'doctor_name' => ['exclude_unless:has_appointment,true', 'required', 'string'],
    ];
}

存在时验证

某些情况下,你可能只想在字段存在于被验证数据中时才执行验证检查。在规则列表中加上 sometimes 规则即可快速实现:

public function rules(): array
{
    return [
        'email' => ['sometimes', 'required', 'email'],
    ];
}

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

如果你要验证的字段应该始终存在但可能为空,请查看关于可选字段的说明。

复杂条件验证

有时候你可能需要基于更复杂的条件逻辑来添加验证规则。例如,你可能只想在另一个字段的值大于 100 时才要求某个字段必填。或者,你可能需要两个字段只在另一个字段存在时才有特定值。

在表单请求中,可以通过覆盖 withValidator() 方法来实现复杂条件验证。该方法在验证器实例创建后、验证执行前被调用,让你有机会动态添加规则:

use Illuminate\Support\Fluent;
use Illuminate\Validation\Validator;

/**
 * 配置验证器实例。
 */
public function withValidator(Validator $validator): void
{
    $validator->sometimes('reason', ['required', 'max:500'], function (Fluent $input) {
        return $input->games >= 100;
    });
}

假设我们的 Web 应用是为游戏收藏家服务的。如果游戏收藏家注册时拥有的游戏超过 100 款,我们希望他们解释为什么拥有这么多游戏。sometimes 方法的第一个参数是要条件验证的字段名,第二个参数是要添加的规则列表,第三个参数是一个闭包——如果返回 true,规则就会被添加。

甚至可以一次为多个字段添加条件验证:

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

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

复杂条件数组验证

有时候你可能想基于同一嵌套数组中另一个字段(索引未知)来验证某个字段。在这种情况下,可以让闭包接收第二个参数——当前被验证的数组中的单个元素:

public function withValidator(Validator $validator): void
{
    $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 验证规则文档所述,array 规则接受一个允许的数组键列表。如果数组中存在任何额外的键,验证将失败:

public function rules(): array
{
    return [
        'user' => ['array:name,username'],
    ];
}

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

验证嵌套数组输入

验证基于嵌套数组的表单输入字段并不复杂。你可以用"点号"语法验证数组中的属性。例如,如果来访 HTTP 请求包含 photos[profile] 字段,可以这样验证:

public function rules(): array
{
    return [
        'photos.profile' => ['required', 'image'],
    ];
}

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

public function rules(): array
{
    return [
        '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\Validation\Rule;

public function rules(): array
{
    return [
        '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 开始)占位符:

public function messages(): array
{
    return [
        '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\Validation\Rules\File;

public function rules(): array
{
    return [
        '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)。

此外,可以使用 dimensions 规则来限制图片的尺寸:

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

public function rules(): array
{
    return [
        'photo' => [
            'required',
            File::image()
                ->min(1024)
                ->max(12 * 1024)
                ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)),
        ],
    ];
}

更多关于验证图片尺寸的信息请查阅dimensions 规则文档。

默认情况下,由于可能存在 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)
)

更多关于验证图片尺寸的信息请查阅dimensions 规则文档。

验证密码

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

use Illuminate\Validation\Rules\Password;

public function rules(): array
{
    return [
        '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-Anonymity 模型来确定密码是否通过 haveibeenpwned.com 服务被泄露,且不会牺牲用户的隐私或安全。

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

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

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

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

定义默认密码规则

你可能会发现在应用的单一位置指定密码的默认验证规则很方便。可以使用 Password::defaults 方法轻松实现,该方法接受一个闭包。传给 defaults 方法的闭包应返回 Password 规则的默认配置。通常,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 方法:

public function rules(): array
{
    return [
        '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 命令创建规则时会自动创建:

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.');
        }
    }
}

定义规则后,就可以在表单请求的 rules() 方法中通过传递规则对象实例来使用它:

use App\Rules\Uppercase;

public function rules(): array
{
    return [
        '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
{
    /**
     * All of the data under validation.
     *
     * @var array<string, mixed>
     */
    protected $data = [];

    // ...

    /**
     * Set the data under validation.
     *
     * @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
{
    /**
     * The validator instance.
     *
     * @var \Illuminate\Validation\Validator
     */
    protected $validator;

    // ...

    /**
     * Set the current validator.
     */
    public function setValidator(Validator $validator): static
    {
        $this->validator = $validator;

        return $this;
    }
}

使用闭包

如果你只需要在整个应用中使用一次自定义规则的功能,可以使用闭包代替规则对象。闭包接收属性名、属性值以及一个在验证失败时应调用的 $fail 回调:

use Closure;

public function rules(): array
{
    return [
        'title' => [
            'required',
            'max:255',
            function (string $attribute, mixed $value, Closure $fail) {
                if ($value === 'foo') {
                    $fail("The {$attribute} is invalid.");
                }
            },
        ],
    ];
}

隐式规则

默认情况下,当被验证的属性不存在或包含空字符串时,包括自定义规则在内的普通验证规则不会运行。例如,unique 规则不会对空字符串运行:

public function rules(): array
{
    return [
        'name' => ['unique:users,name'],
    ];
}

// 如果 name 为 '',验证将通过

要让自定义规则在属性为空时也运行,规则必须暗示该属性是必填的。用 make:rule Artisan 命令的 --implicit 选项可以快速生成新的隐式规则对象:

php artisan make:rule Uppercase --implicit

"隐式"规则只是_暗示_该属性是必填的。是否真的将缺失或空的属性标记为无效,由你决定。