表单验证


简介

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 App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
 
class PostController extends Controller
{
    /**
     * Show the form to create a new blog post.
     */
    public function create(): View
    {
        return view('post.create');
    }
 
    /**
     * Store a new blog post.
     */
    public function store(Request $request): RedirectResponse
    {
        // Validate and store the blog post...
 
        $post = /** ... */
 
        return to_route('post.show', ['post' => $post->id]);
    }
}

编写验证逻辑

现在我们准备用验证新博客文章输入的逻辑填充 store 方法。我们使用 Illuminate\Http\Request 对象提供的 validate 方法来实现这一功能,如果验证规则通过,代码将会继续往下执行;反之,如果验证失败,将会抛出一个异常,相应的错误响应也会自动发送给用户。在这个传统的 HTTP 请求案例中,将会生成一个重定向响应,如果是 AJAX 请求则会返回一个 JSON 响应。

要更好地理解 validate 方法,让我们回顾下 store 方法:

/**
 * 存储博客文章
 */
public function store(Request $request){
    $validatedData = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // 验证通过,存储到数据库...
    return redirect('/posts');
}

正如你所看到的,我们只是传入期望的验证规则到 validate 方法。再强调一次,如果验证失败,相应的响应会自动生成。如果验证通过,控制器将会继续往下执行。

此外,验证规则还可以通过数组方式指定,而不是通过 | 分隔的字符串:

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

还可以使用 validateWithBag 方法在一个命名错误包中验证请求并存储错误消息:

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

首次验证失败后中止后续规则验证

有时候你可能想要在首次验证失败后停止检查该属性的其它验证规则,要实现这个功能,可以在规则属性中分配 bail 作为首规则:

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

在这个例子中,如果 title 属性上的 required 规则验证失败,则不会检查 unique 规则,规则会按照分配顺序依次进行验证。

嵌套属性注意事项

如果 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 将自动将用户重定向回其以前的位置。此外,所有验证错误和请求输入都将自动闪存到会话中。

通过 Illuminate\View\Middleware\ShareErrorsFromSession 中间件,$errors 变量会在应用程序的所有视图之间共享,这个中间件是由 web 中间件组提供的。当应用该中间件时,$errors 变量将始终在视图中可用,从而使您方便地假定 $errors 变量始终被定义并且可以安全使用。$errors 变量将是 Illuminate\Support\MessageBag 的一个实例。有关如何使用此对象的更多信息,请查看其文档

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

<!-- /resources/views/post/create.blade.php -->
 
<h1>Create Post</h1>
 
@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif
 
<!-- Create Post Form -->

自定义错误消息

Laravel 内置的验证规则都有对应的错误消息,它们位于应用的 lang/en/validation.php 文件中。在这个文件中,你会找到每个验证规则的翻译条目。你可以根据你的应用需求自由更改或修改这些消息。

此外,你可以将此文件复制到另一个语言目录以翻译应用程序语言的消息。要了解有关 Laravel 本地化的更多信息,请查看完整的本地化文档

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

XHR 请求&验证

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

@error指令

你还可以使用 Blade 提供的 @error 指令快速检查给定属性是否存在验证错误信息,在 @error 指令中,你可以输出 $message 变量来显示错误信息:

<!-- /resources/views/post/create.blade.php -->
 
<label for="title">Post Title</label>
 
<input id="title"
    type="text"
    name="title"
    class="@error('title') is-invalid @enderror">
 
@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

@error 指令用法中,如果您使用了命名错误包,可以将错误包的名称作为第二个参数传递给该指令:

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

重新填充表单

Laravel 自动生成重定向响应以响应验证错误,这样您就可以方便地在下一个请求中访问输入数据并重新填充用户试图提交的表单。

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

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

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

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

可选字段注意事项

默认情况下,Laravel自带了 TrimStringsConvertEmptyStringsToNull 中间件,这两个中间件位于 App\Http\Kernel 类的全局中间件堆栈中,因为这个原因,你需要经常将“可选”的请求字段标记为 nullable —— 如果你不想让验证器将 null 判定为无效的话。例如:

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

在这个例子中,我们指定 publish_at 字段可以为 null 或者有效的日期格式。如果 nullable 没有被添加到验证规则,验证器会将 null 判定为无效日期。

验证错误消息格式

当您的应用程序引发 Illuminate\Validation\ValidationException 异常并且传入的 HTTP 请求需要 JSON 响应时,Laravel 将自动为您格式化错误消息并返回 422 Unprocessable Entity HTTP 响应。

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

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

表单请求验证

创建表单请求

对于更复杂的验证场景,你可能想要创建一个“表单请求”。表单请求是包含验证逻辑的自定义请求类,要创建表单验证类,可以使用 Artisan 命令 make:request

php artisan make:request StoreBlogPost

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

正如您可能已经猜到的那样,authorize 方法负责确定当前经过身份验证的用户是否可以执行该请求所代表的操作,而 rules 方法返回应该应用于请求数据的验证规则:

/**
 * Get the validation rules that apply to the request.
 *
 * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
 */
public function rules(): array
{
    return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ];
}

注:你可以在 rules 方法签名签名中注入任何依赖,它们会通过服务容器自动解析。

那么,验证规则如何生效呢?你所要做的就是在控制器方法中类型提示该请求类。这样表单输入请求会在控制器方法被调用之前被验证,这就是说你不需要将控制器方法和验证逻辑杂糅在一起:

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

如果验证失败,重定向响应会被生成并将用户退回上一个位置,错误信息也会被存储到一次性 Session 以便在视图中显示。如果是 AJAX 请求,带 422 状态码的 HTTP 响应将会返回给用户,该响应数据中还包含了 JSON 格式的验证错误信息

添加验证后钩子到表单请求

如果你想要添加“验证后”钩子到表单请求,可以使用 withValidator 方法。该方法接收完整的构造验证器,从而允许你在验证规则执行前调用任何验证器方法:

use Illuminate\Validation\Validator;
 
/**
 * Configure the validator instance.
 */
public function withValidator(Validator $validator): void
{
    $validator->after(function (Validator $validator) {
        if ($this->somethingElseIsInvalid()) {
            $validator->errors()->add('field', 'Something is wrong with this field!');
        }
    });
}

停止在第一个验证失败属性上

通过在请求类中添加 stopOnFirstFailure 属性,您可以告知验证器,在发生单个验证失败后应停止验证所有属性:

/**
 * Indicates if the validator should stop on the first rule failure.
 *
 * @var bool
 */
protected $stopOnFirstFailure = true;

自定义重定向位置

如先前所述,当表单请求验证失败时,将生成重定向响应将用户发送回其先前的位置。但是,您可以自定义此行为。要这样做,请在表单请求中定义 $redirect 属性:

/**
 * The URI that users should be redirected to if validation fails.
 *
 * @var string
 */
protected $redirect = '/dashboard';

如果你希望将用户重定向到具名路由,可以在表单请求中定义 $redirectRoute 属性:

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

授权表单请求

表单请求类还包含了一个 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 即可:

/**
 * 判断请求用户是否经过授权
 */
public function authorize(){
    return true;
}

注:你可以在 authorize() 方法签名中注入任何依赖,它们将会通过服务容器自动解析。

自定义错误消息

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

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

自定义验证属性

如果你想要将验证消息中的 :attribute 部分替换为自定义的属性名,可以通过重写 attributes 方法来指定自定义的名称。该方法会返回属性名及对应自定义名称键值对数组:

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

为验证准备输入

如果你需要在应用验证规则之前对所有来自用户请求的数据进行清理和消毒,可以使用 prepareForValidation 方法:

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

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

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

手动创建验证器

如果你不想使用请求实例上的 validate 方法,可以使用 Validator 门面手动创建一个验证器实例,该门面提供的 make 方法可用于生成一个新的验证器实例:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
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 方法将错误数据存放到一次性 Session,使用该方法时,$errors 变量重定向后自动在视图间共享,从而允许你轻松将其显示给用户,withErrors 方法接收一个验证器、或者一个 MessageBag ,又或者一个 PHP 数组。

在第一次验证失败时停止

stopOnFirstFailure 方法将告诉验证器,一旦发生单个验证失败,就应停止验证所有属性:

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

自动重定向

如果你想要手动创建一个验证器实例,但仍然使用请求实例的 validate 方法提供的自动重定向,可以调用已存在验证器实例上的 validate 方法,如果验证失败,用户将会被自动重定向,或者,如果是 AJAX 请求的话,返回 JSON 响应

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

如果验证失败,你可以使用 validateWithBag 方法在命名错误包中存储错误消息:

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

命名错误包

如果你在单个页面上有多个表单,可能需要命名错误的 MessageBag,从而允许你为指定表单获取错误信息。只需要传递名称作为第二个参数给 withErrors 即可:

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

然后你就可以从 $errors 变量中访问命名的 MessageBag 实例:

{{ $errors->login->first('email') }}

自定义错误消息

如果需要的话,您可以提供自定义错误消息,让验证器实例使用它们而不是 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 方法即可:

use Illuminate\Support\Facades;
use Illuminate\Validation\Validator;
 
$validator = Facades\Validator::make(/* ... */);
 
$validator->after(function (Validator $validator) {
    if ($this->somethingElseIsInvalid()) {
        $validator->errors()->add(
            'field', 'Something is wrong with this field!'
        );
    }
});
 
if ($validator->fails()) {
    // ...
}

处理验证后的输入

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

$validated = $request->validated();

$validated = $validator->validated();

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

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

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

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

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

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

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

$email = $validated['email'];

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

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

如果您想要将经过验证的数据作为一个集合实例检索,可以调用collect方法:

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

处理错误消息

调用 Validator 实例上的 errors 方法之后,将会获取一个 Illuminate\Support\MessageBag 实例,该实例中包含了多种处理息的便利方法。在所有视图中默认有效的 $errors 变量也是一个 MessageBag 实例。

获取某字段的第一条错误信息

要获取指定字段的第一条错误信息,可以使用 first 方法:

$errors = $validator->errors();
echo $errors->first('email');

获取指定字段的所有错误信息

如果你想要简单获取指定字段的所有错误信息数组,使用 get 方法:

foreach ($errors->get('email') as $message) {
    //
}

如果是一个数组表单字段,可以使用 * 获取所有数组元素错误信息:

foreach ($errors->get('attachments.*') as $message) {
    //
}

获取所有字段的所有错误信息

要获取所有字段的所有错误信息,可以使用 all 方法:

foreach ($errors->all() as $message) {
    //
}

判断消息中是否存在某字段的错误信息

has 方法可用于判断错误信息中是否包含给定字段:

if ($errors->has(’email’)) {
    //
}

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

Laravel 内置的验证规则每个都有一个错误消息,该消息位于您的应用程序的 lang/en/validation.php 文件中。在该文件中,您会找到每个验证规则的翻译条目。您可以根据应用程序的需求更改或修改这些消息。

此外,您可以将此文件复制到另一个语言目录中,以翻译应用程序语言的消息。有关 Laravel 本地化的更多信息,请查看完整的本地化文档

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

针对特定属性的自定义消息

您可以在应用程序的验证语言文件的 custom 数组中自定义用于指定属性和规则组合的错误消息。为此,请将消息自定义添加到应用程序的 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'
]);

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

The credit card number field is required when payment type is 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
  • AcceptedIf
  • Active URL
  • After (Date)
  • After Or Equal(Date)
  • Alpha
  • Alpha Dash
  • Alpha Numeric
  • Array
  • Ascii
  • Bail
  • Before (Date)
  • Before Or Equal(Date)
  • Between
  • Boolean
  • Confirmed
  • Current Password
  • Date
  • Date Equals
  • Date Format
  • Decimal
  • Declined
  • Declined If
  • Different
  • Digits
  • Digits Between
  • Dimensions (Image Files)
  • Distinct
  • Doesnt Start With
  • Doesnt End With
  • E-Mail
  • Ends With
  • Enum
  • Exclude If
  • Exclude Unless
  • Exclude With
  • Exclude Without
  • Exists (Database)
  • File
  • Filled
  • Greater Than
  • Greater Than Or Equal
  • Image (File)
  • In
  • In Array
  • Integer
  • IP Address
  • JSON
  • Less Than
  • Less Than Or Equal
  • Lowercase
  • MAC Address
  • Max
  • Max Digits
  • MIME Types
  • MIME Type By File Extension
  • Min
  • Min Digits
  • Missing
  • Missing If
  • Missing Unless
  • Missing With
  • Missing With All
  • Multiple Of
  • Not In
  • Not Regex
  • Nullable
  • Numeric
  • Password
  • Present
  • Prohibited
  • Prohibited If
  • Prohibited Unless
  • Prohibits
  • Regular Expression
  • Required
  • Required If
  • Required Unless
  • Required With
  • Required With All
  • Required Without
  • Required Without All
  • Required Array Keys
  • Same
  • Size
  • Sometimes
  • Starts With
  • String
  • Timezone
  • Unique (Database)
  • Uppercase
  • URL
  • ULID
  • UUID

accepted

验证字段的值必须是 yeson1true,这在「同意服务协议」时很有用。

accepted_if:anotherfield,value,...

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

active_url

验证字段必须是基于 PHP 函数 dns_get_record 的,有 A 或 AAAA 记录的值。URL 中的主机名在传递给 dns_get_record 之前使用 PHP 函数 parse_url 解析。

after:date

验证字段必须是给定日期之后的一个值,日期将会通过 PHP 函数 strtotime 传递:

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

你可以指定另外一个与日期进行比较的字段,而不是传递一个日期字符串给 strtotime 执行:

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

after_or_equal:date

验证字段必须是大于等于给定日期的值,更多信息,请参考 after:date 规则。

alpha

该验证规则要求被验证字段只能包含 Unicode 字母字符,即 \p{L}\p{M}。如果需要将验证规则限制为 ASCII 字符范围内的字符(a-z 和 A-Z),可以在验证规则中提供 ascii 选项:

'username' => 'alpha:ascii',

alpha_dash

该验证规则验证的字段必须完全由 Unicode 字母数字字符组成,包括 \p{L}\p{M}\p{N},以及 ASCII 破折号(-)和 ASCII 下划线(_)。

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

'username' => 'alpha_dash:ascii',

alpha_num

该验证规则要求验证字段完全由 Unicode 字母数字字符组成,包括 \p{L}\p{M}\p{N}

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

'username' => 'alpha_num:ascii',

array

该验证规则要求验证的字段必须是 PHP 数组。

如果数组规则提供了附加值,则输入数组中的每个键必须存在于数组规则提供的值列表中。在下面的示例中,输入数组中的 admin 键是无效的,因为它不在数组规则提供的值列表中:

use Illuminate\Support\Facades\Validator;

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

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

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

ascii

该验证规则要求验证字段全部由7位ASCII字符组成。

bail

停止在字段验证失败后继续运行验证规则。

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

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

before:date

after:date 相对,验证字段必须是指定日期之前的一个数值,日期将会传递给 PHP strtotime 函数。

before_or_equal:date

该验证字段的值必须是早于或等于给定日期的值。日期将传递到 PHP strtotime 函数中,以便将其转换为有效的 DateTime 实例。此外,与“after”规则类似,验证中可以提供另一个字段的名称作为日期的值。

between:min,max

验证字段大小在给定的最小值和最大值之间,字符串、数字、数组和文件都可以像使用 size 规则一样使用该规则:

'name' => 'required|between:1,20'

boolean

验证字段必须可以被转化为布尔值,接收 true, false, 1, 0, "1""0" 等输入。

confirmed

验证字段必须有一个匹配字段 foo_confirmation,例如,如果验证字段是 password,必须输入一个与之匹配的 password_confirmation 字段。

current_password

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

'password' => 'current_password:api'

date

验证字段必须是一个基于 PHP strtotime 函数的有效日期

date_equals:date

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

date_format:format,...

验证字段必须匹配指定格式,可以使用 PHP 函数 datedate_format 验证该字段。该验证规则支持 PHP DateTime 类支持的所有格式。

decimal:min,max

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

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

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

declined

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

declined_if:anotherfield,value,...

如果另一个验证字段的值等于指定值,则该验证字段的值必须为 "no""off"0false

different:field

验证字段必须是一个和指定字段不同的值。

digits:value

验证字段必须是数字且长度为 value 指定的值。

digits_between:min,max

验证字段数值长度必须介于最小值和最大值之间。

dimensions

验证的图片尺寸必须满足该规定参数指定的约束条件:

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

有效的约束条件包括:min_width, max_width, min_height, max_height, width, height, ratio

ratio 约束宽度/高度的比率,这可以通过表达式 3/2 或浮点数 1.5 来表示:

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

由于该规则要求多个参数,可以使用 Rule::dimensions 方法来构造该规则:

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

distinct

处理数组时,验证字段不能包含重复值:

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

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

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

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

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

doesnt_start_with:foo,bar,...

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

doesnt_end_with:foo,bar,...

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

email

验证字段必须是格式正确的电子邮件地址。在底层,该验证规则使用了 egulias/email-validator 包来验证邮箱地址,并且默认使用的是 RFCValidation 验证器,如果你想使用其他邮箱验证风格,可以这样指定:

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

上述示例会使用 RFCValidationDNSCheckValidation 验证器,以下是所有支持的验证器列表:

  • rfcRFCValidation
  • strictNoRFCWarningsValidation
  • dnsDNSCheckValidation
  • spoofSpoofCheckValidation
  • filterFilterEmailValidation
  • filter_unicode: FilterEmailValidation::unicode()

filter 验证器使用的是 PHP 内置的 filter_var 方法,Laravel 5.8 之前使用的都是这个验证器。

dnsspoof 验证器需要安装 PHP intl 扩展。

ends_with: foo,bar,...

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

enum

Enum 规则是一个基于类的规则,用于验证验证字段是否包含有效的枚举值。Enum 规则接受枚举名称作为其唯一的构造函数参数:

use App\Enums\ServerStatus;
use Illuminate\Validation\Rules\Enum;
 
$request->validate([
    'status' => [new Enum(ServerStatus::class)],
]);

exclude

该验证字段将从 validatevalidated 方法返回的请求数据中排除。

exclude_if:anotherfield,value

如果 anotherfield 字段等于 value,则该验证字段将从 validatevalidated 方法返回的请求数据中排除。

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

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
 
Validator::make($request->all(), [
    'role_id' => Rule::excludeIf($request->user()->is_admin),
]);
 
Validator::make($request->all(), [
    'role_id' => Rule::excludeIf(fn () => $request->user()->is_admin),
]);

exclude_unless:anotherfield,value

除非 anotherfield 字段等于 value,否则该验证字段将从 validatevalidated 方法返回的请求数据中排除。如果 valuenullexclude_unless:name,null),则该验证字段将从 validatevalidated 方法返回的请求数据中排除,除非比较字段为 null 或比较字段在请求数据中不存在。

exclude_with:anotherfield

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

exclude_without:anotherfield

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

exists:table,column

验证字段必须存在于指定数据表。

基本使用:

'state' => 'exists:states'

如果 column 选项没有指定,将会使用字段名。

指定自定义列名:

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

有时,你可能需要为 exists 查询指定要使用的数据库连接,这可以在表名前通过.前置数据库连接来实现:

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

除了直接指定表名外,你也可以指定用于确定表名的 Eloquent 模型:

'user_id' => 'exists:App\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) {
            return $query->where('account_id', 1);
        }),
    ],
]);

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

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

file

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

filled

验证字段如果存在则不能为空。

gt:field

验证字段必须大于给定 field 字段,这两个字段类型必须一致,适用于字符串、数字、数组和文件,和 size 规则类似。

gte:field

验证字段必须大于等于给定 field 字段,这两个字段类型必须一致,适用于字符串、数字、数组和文件,和 size 规则类似。

image

验证文件必须是图片(jpeg、png、bmp、gif、svg 或者 webp)。

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中存在。

integer

验证字段必须是整型。

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

ip

验证字段必须是IP地址。

ipv4

验证字段必须是IPv4地址。

ipv6

验证字段必须是IPv6地址。

json

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

lt:field

验证字段必须小于给定 field 字段,这两个字段类型必须一致,适用于字符串、数字、数组和文件,和 size 规则类似。

lte:field

验证字段必须小于等于给定 field 字段,这两个字段类型必须一致,适用于字符串、数字、数组和文件,和 size 规则类似。

lowercase

该验证字段的值必须是小写。

mac_address

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

max:value

验证字段必须小于等于最大值,和字符串、数值、数组、文件字段的 size 规则使用方式一样。

max_digits:value

该验证字段的整数值的最大长度必须为 value

mimetypes:text/plain,...

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

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

为了判断上传文件的 MIME 类型,框架将会读取文件内容来猜测 MIME 类型,这可能会和客户端 MIME 类型不同。

mimes:foo,bar,...

验证文件的 MIME 类型必须是该规则列出的扩展类型中的一个

MIME 规则的基本使用:

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

尽管你只是指定了扩展名,该规则实际上验证的是通过读取文件内容获取到的文件 MIME 类型。

完整的 MIME 类型列表及其相应的扩展可以在这里找到:http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types

min:value

max:value 相对,验证字段必须大于等于最小值,对字符串、数值、数组、文件字段而言,和 size 规则使用方式一致。

min_digits:value

该验证字段的整数值的最小长度必须为 value。

multiple_of:value

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

missing

该验证字段不应该出现在输入数据中。

missing_if:anotherfield,value,...

如果 anotherfield 字段等于任何值,则该验证字段不应该出现在输入数据中。

missing_unless:anotherfield,value

除非 anotherfield 字段等于任何值,否则该验证字段不应该出现在输入数据中。

missing_with:foo,bar,...

只有其他指定的字段中有任何一个存在时,该验证字段才不应该出现在输入数据中。

missing_with_all:foo,bar,...

只有所有其他指定的字段都存在时,该验证字段才不应该出现在输入数据中。

not_in:foo,bar,…

验证字段值不能在给定列表中,和 in 规则类似,我们可以使用 Rule::notIn 方法来构建规则:

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

not_regex:pattern

验证字段不能匹配给定正则表达式。

在底层,该规则使用了 PHP preg_match 函数,指定的模式也必须遵循 preg_match 函数要求的格式,并且包含有效的分隔符,例如:

'email' => 'not_regex:/^.+$/i'

注:使用 regex/not_regex 模式时,规则必须放在数组中,而不能使用管道分隔符,尤其是正则表达式中包含管道符号时。

nullable

验证字段可以是 null,这在验证一些可以为 null 的原始数据如整型或字符串时很有用。

numeric

验证字段必须是数值。

password

被验证字段必须匹配认证用户的密码,你可以使用该规则的第一个参数指定一个认证守卫(guard):

'password' => 'password:api'

该规则已重命名为 current_password,旨在在 Laravel 9 中删除它。请改用 Current Password 规则。

present

验证字段必须出现在输入数据中但可以为空。

prohibited

该验证字段必须丢失或为空。如果符合以下条件之一,则字段为空:

  • 该值为 null
  • 该值为空字符串。
  • 该值是空数组或空可计数对象。
  • 该值是具有空路径的上传文件。

prohibited_if:anotherfield,value,...

如果 anotherfield 字段等于任何值,则该验证字段必须丢失或为空。如果符合以下条件之一,则字段为空:

  • 该值为 null
  • 该值为空字符串。
  • 该值是空数组或空可计数对象。
  • 该值是具有空路径的上传文件。

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

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
 
Validator::make($request->all(), [
    'role_id' => Rule::prohibitedIf($request->user()->is_admin),
]);
 
Validator::make($request->all(), [
    'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin),
]);

prohibited_unless:anotherfield,value,...

除非 anotherfield 字段等于任何值,否则该验证字段必须丢失或为空。如果符合以下条件之一,则字段为空:

  • 该值为 null。
  • 该值为空字符串。
  • 该值是空数组或空可计数对象。
  • 该值是具有空路径的上传文件。

prohibits:anotherfield,...

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

  • 该值为 null。
  • 该值为空字符串。
  • 该值是空数组或空可计数对象。
  • 该值是具有空路径的上传文件。

regex:pattern

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

该规则底层使用的是 PHP 的 preg_match 函数。因此,指定的模式需要遵循 preg_match 函数所要求的格式并且包含有效的分隔符。例如:

'email' => 'regex:/^.+@.+$/i'`

使用 regex/not_regex 模式时,规则必须放在数组中,而不能使用管道分隔符 |,尤其是正则表达式中包含管道符号时。

required

验证字段值不能为空,以下情况字段值都为空:

  • 值为null
  • 值是空字符串
  • 值是空数组或者空的 Coutable 对象
  • 值是上传文件但路径为空

required_if:anotherfield,value,…

验证字段在 anotherfield 等于指定值 value 时必须存在且不能为空。

如果你想要为 required_if 规则构造更复杂的条件,可以使用 Rule::requiredIf 方法,该方法接收一个布尔值或闭包。当传递一个闭包时,会返回 truefalse 以表明验证字段是否是必须的:

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_unless:anotherfield,value,…

除非 anotherfield 字段等于任何值,否则该验证字段必须存在且非空。这也意味着,除非 valuenull,否则请求数据中必须存在 anotherfield 字段。如果 valuenullrequired_unless:name,null),则除非比较字段为 null 或比较字段在请求数据中不存在,否则必须要求验证字段存在。

required_with:foo,bar,…

验证字段只有在任一其它指定字段存在的情况才是必须的。

required_with_all:foo,bar,…

验证字段只有在所有指定字段存在的情况下才是必须的。

required_without:foo,bar,…

验证字段只有当任一指定字段不存在的情况下才是必须的。

required_without_all:foo,bar,…

验证字段只有当所有指定字段不存在的情况下才是必须的。

required_array_keys:foo,bar,...

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

same:field

给定字段和验证字段必须匹配。

size:value

验证字段必须有和给定值 value 相匹配的尺寸/大小,对字符串而言,value 是相应的字符数目;对数值而言,value 是给定整型值;对数组而言,value 是数组长度;对文件而言,value 是相应的文件千字节数(KB)。我们来看一些例子:

// 验证字符串长度是否是12..
'title' => 'size:12';
    
// 验证给定整型值是否等于10...
'seats' => 'integer|size:10';
    
// 验证数组是否包含5个元素...
'tags' => 'array|size:5';
    
// 验证上传文件是否是 512 KB...
'image' => 'file|size:512';

starts_with:foo,bar,...

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

string

验证字段必须是字符串,如果允许字段为空,需要分配 nullable 规则到该字段。

timezone

验证字符必须是基于 PHP 函数 timezone_identifiers_list 的有效时区标识

unique:table,column,except,idColumn

验证字段在给定数据表上必须是唯一的,如果不指定 column 选项,字段名将作为默认 column

1)指定自定义列名:

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

2)自定义数据库连接:

有时候,你可能需要自定义验证器生成的数据库连接,正如上面所看到的,设置 unique:users 作为验证规则将会使用默认数据库连接来查询数据库。要覆盖默认连接,在数据表名后使用“.”指定连接:

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

3)强制一个忽略给定 ID 的唯一规则:

有时候,你可能希望在唯一检查时忽略给定 ID,例如,考虑一个包含用户名、邮箱地址和位置的”更新属性“界面,你将要验证邮箱地址是唯一的,然而,如果用户只改变用户名字段而并没有改变邮箱字段,你不想要因为用户已经拥有该邮箱地址而抛出验证错误,你只想要在用户提供的邮箱已经被别人使用的情况下才抛出验证错误。

要告诉验证器忽略用户 ID,可以使用 Rule 类来定义这个规则,我们还要以数组方式指定验证规则,而不是使用 | 来界定规则:

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

永远不要传递用户可以控制的请求输入到 ignore 方法中,取而代之地,你只能传递一些系统生成的 UUID 比如来自 Eloquent 模型实例的自增 ID 或者 UUID。否则,你的应用就会存在 SQL 注入攻击风险。

除了传递模型实例主键值到 ignore 方法之外,你还可以传递整个模型实例。Laravel 会自动从模型实例中解析出主键值:

Rule::unique('users')->ignore($user)

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

'email' => Rule::unique('users')->ignore($user->id, 'user_id')

默认情况下,unique 规则会检查与要验证的属性名匹配的列的唯一性。不过,你可以指定不同的列名作为 unique 方法的第二个参数:

Rule::unique('users', 'email_address')->ignore($user->id),

4)添加额外的 where 子句:

使用 where 方法自定义查询的时候还可以指定额外查询约束,例如,下面我们来添加一个验证 account_id1 的约束:

'email' => Rule::unique('users')->where(function ($query) {
    $query->where('account_id', 1);
})

uppercase

该验证字段必须是大写字母。

url

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

ulid

该验证字段必须是有效的 Universally Unique Lexicographically Sortable Identifier (ULID)。

uuid

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

添加条件规则

当字段包含特定值时跳过验证

有时候,如果另一个字段包含给定值的话,你可能不想要验证给定字段,这可以通过 exclude_if 规则来实现。在本例中,如果 has_appointment 字段包含 false 值的话,appointment_datedoctor_name 字段将不会被验证:

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

此外,还可以使用 exclude_unless 规则在除非另一个字段包含某个值的话才验证给定字段:

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

存在时验证

在某些场景下,你可能想要只有某个字段存在的情况下进行验证检查,要快速实现这个,添加 sometimes 规则到规则列表:

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

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

注:如果你尝试验证一个总是存在但可能为空的字段时,参考可选字段注意事项

复杂条件验证

有时候你可能想要基于更复杂的条件逻辑添加验证规则。例如,你可能想要只有在另一个字段值大于 100 时才要求一个给定字段是必须的,或者,你可能需要只有当另一个字段存在时两个字段才都有给定值。添加这个验证规则并不是一件头疼的事。首先,创建一个永远不会改变的静态规则到 Validator 实例:

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

让我们假定我们的 Web 应用服务于游戏收藏者。如果一个游戏收藏者注册了我们的应用并拥有超过 100 个游戏,我们想要他们解释为什么他们会有这么多游戏,例如,也许他们在运营一个游戏二手店,又或者他们只是喜欢收藏。要添加这种条件,我们可以使用 Validator 实例上的 sometimes 方法:

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

传递给 sometimes 方法的第一个参数是我们需要有条件验证的名称字段,第二个参数是我们想要添加的规则,如果作为第三个参数的闭包返回 true,规则被添加。该方法让构建复杂条件验证变得简单,你甚至可以一次为多个字段添加条件验证:

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

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

复杂的条件数组验证

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

$input = [
    'channels' => [
        [
            'type' => 'email',
            'address' => 'abigail@example.com',
        ],
        [
            'type' => 'url',
            'address' => 'https://example.com',
        ],
    ],
];
 
$validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) {
    return $item->type === 'email';
});
 
$validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) {
    return $item->type !== 'email';
});

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

验证数组

在数组验证规则文档中讨论过,array规则接受允许的数组键列表。如果数组中有任何其他键,则验证将失败:

use Illuminate\Support\Facades\Validator;
 
$input = [
    'user' => [
        'name' => 'Taylor Otwell',
        'username' => 'taylorotwell',
        'admin' => true,
    ],
];
 
Validator::make($input, [
    'user' => 'array:username,locale',
]);

一般来说,您应该始终指定允许出现在您的数组中的键。否则,验证器的validate和validated方法将返回所有已验证的数据,包括数组和其所有键,即使这些键没有被其他嵌套数组验证规则验证。

验证嵌套数组输入

验证嵌套数组的表单输入字段并不难。您可以使用“点符号”验证数组中的属性。例如,如果传入的 HTTP 请求包含一个 photos[profile] 字段,则可以像下面这样验证它:

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

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

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

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

'custom' => [
    'person.*.email' => [
        'unique' => 'Each person 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开始)占位符:

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

验证文件

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

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rules\File;
 
Validator::validate($input, [
    'attachment' => [
        'required',
        File::types(['mp3', 'wav'])
            ->min(1024)
            ->max(12 * 1024),
    ],
]);

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

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

有关验证图像尺寸的更多信息,请参见 dimension 规则文档

文件类型

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

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

验证密码

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

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rules\Password;
 
$validator = Validator::make($request->all(), [
    'password' => ['required', 'confirmed', Password::min(8)],
]);

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

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

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

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

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

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

此外,您可以使用 uncompromised 方法确保密码没有在公共密码数据泄露泄漏:

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

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

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

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

当然,您可以在上述示例中链接所有方法:

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

定义默认密码规则

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

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]);

    // ...
});

自定义验证规则

使用 Rule 对象

如上所述,Laravel 提供了多种有用的验证规则;不过,你可能还是需要指定一些自己的验证规则。注册自定义验证规则的一种方法是使用规则对象,要生成一个新的规则对象,可以使用 Artisan 命令 make:rule。下面我们使用这个命令来生成一个用于验证字符串是否是大写的规则,生成的新规则对象类位于 app/Rules 目录:

php artisan make:rule Uppercase

规则创建之后,就可以定义行为方法,规则对象包含一个 validate 方法。该方法接收属性名称、其值和一个回调函数作为参数。在验证失败时,应调用回调函数并传递验证错误消息:

<?php
 
namespace App\Rules;
 
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
 
class Uppercase implements ValidationRule
{
    /**
     * Run the validation rule.
     */
    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
{
    /**
     * 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 Illuminate\Support\Facades\Validator;

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

隐式规则

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

use Illuminate\Support\Facades\Validator;
 
$rules = ['name' => 'unique:users,name'];
 
$input = ['name' => ''];
 
Validator::make($input, $rules)->passes(); // true

对于一个即使属性为空也需要运行的自定义规则,该规则必须意味着属性是必需的。您可以使用 make:rule Artisan 命令并带有 --implicit 选项来快速生成一个新的隐式规则对象:

php artisan make:rule Uppercase --implicit

“隐式”规则仅意味着该属性是必需的。是否实际上使缺少或空的属性无效取决于您。


点赞 取消点赞 收藏 取消收藏

<< 上一篇: Session

>> 下一篇: 错误处理