翻譯進度
94% 已翻譯
更新時間:
2024年6月30日 上午8:27:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

表單驗證 - Validation

簡介

Laravel 中提供了多種不同方式能讓我們對連入資料做驗證 (Validate)。最常見的方法就是使用所有連入 HTTP Request 上都有的 validata 方法來驗證。不過,我們稍後也會討論其他驗證方法。

Laravel 中包含了多種方便的驗證規則可讓你套用到資料上,甚至,Laravel 中也有辦法驗證某個值在給定資料表上是否是不重複的。稍後我們會詳細說明各個驗證規則,讓你熟悉 Laravel 中所有的驗證功能。

「Validation」快速開始

要瞭解有關 Laravel 中強大的驗證功能,我們先來看看一個驗證表單並將錯誤訊息顯示給使用者看的完整範例。閱讀這個高階的範例,讀者將能對如何使用 Laravel 驗證連入的 Request 資料有個基本的理解:

定義 Route

首先,我們假設 routes/web.php 檔案中有下列 Route 定義:

1use App\Http\Controllers\PostController;
2 
3Route::get('/post/create', [PostController::class, 'create']);
4Route::post('/post', [PostController::class, 'store']);
1use App\Http\Controllers\PostController;
2 
3Route::get('/post/create', [PostController::class, 'create']);
4Route::post('/post', [PostController::class, 'store']);

這個 GET Route 會向使用者顯示一個用來建立新部落格貼文的表單,而 POST Route 則用來將新部落格貼文儲存到資料庫中。

建立 Controller

接著,我們來看看一個簡單的 Controller,這個 Controller 用來處理這些 Route 的連入 Request。我們現在先把 store 方法留空:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use Illuminate\Http\RedirectResponse;
7use Illuminate\Http\Request;
8use Illuminate\View\View;
9 
10class PostController extends Controller
11{
12 /**
13 * Show the form to create a new blog post.
14 */
15 public function create(): View
16 {
17 return view('post.create');
18 }
19 
20 /**
21 * Store a new blog post.
22 */
23 public function store(Request $request): RedirectResponse
24 {
25 // 驗證並保存部落格貼文...
26 
27 $post = /** ... */
28 
29 return to_route('post.show', ['post' => $post->id]);
30 }
31}
1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use Illuminate\Http\RedirectResponse;
7use Illuminate\Http\Request;
8use Illuminate\View\View;
9 
10class PostController extends Controller
11{
12 /**
13 * Show the form to create a new blog post.
14 */
15 public function create(): View
16 {
17 return view('post.create');
18 }
19 
20 /**
21 * Store a new blog post.
22 */
23 public function store(Request $request): RedirectResponse
24 {
25 // 驗證並保存部落格貼文...
26 
27 $post = /** ... */
28 
29 return to_route('post.show', ['post' => $post->id]);
30 }
31}

撰寫驗證邏輯

現在,我們已經準備好可以在 store 方法內撰寫驗證新部落格貼文的驗證邏輯了。要撰寫驗證邏輯,我們會使用 Illuminate\Http\Request 物件所提供的 validate 方法。若驗證規則通過,則程式碼就可以繼續正常執行。不過,若驗證失敗,則會擲回 Illuminate\Validation\ValidationException 例外,然後 Laravel 會自動回傳適當的錯誤 Response 給使用者。

若驗證失敗時使用的是傳統 HTTP Request,則會產生一個回到上一頁網址的 Redirect Response。若連入的 Request 是 XHR Request,則會回傳一個[包含驗證錯誤訊息的 JSON Response]](#validation-error-response-format)。

為了更好瞭解 validate,我們先回來看看 store 方法:

1/**
2 * Store a new blog post.
3 */
4public function store(Request $request): Response
5{
6 $validated = $request->validate([
7 'title' => 'required|unique:posts|max:255',
8 'body' => 'required',
9 ]);
10 
11 // 部落格貼文有效...
12 
13 return response()->noContent();
14}
1/**
2 * Store a new blog post.
3 */
4public function store(Request $request): Response
5{
6 $validated = $request->validate([
7 'title' => 'required|unique:posts|max:255',
8 'body' => 'required',
9 ]);
10 
11 // 部落格貼文有效...
12 
13 return response()->noContent();
14}

就像我們可以看到的一樣,我們將驗證規則傳入 validate 方法。別擔心 —— 所有可用的規則都有說明文件。一樣,若驗證失敗,會自動產生適當的 Response。若驗證成功,我們的 Controller 就會繼續正常執行。

或者,我們也可以不使用以 | 分隔的單一字串來指定驗證規則,而是使用一組規則陣列:

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

此外,也可以使用 validateWithBag 方法來驗證 Request 並將錯誤訊息保存在一個命名的 Error Bag

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

在第一個驗證失敗後就停止

有時候,我們會想在驗證某個屬性時,當遇到第一個驗證失敗就停止執行接下來的驗證規則。為此,可以在該屬性上加上 bail 規則:

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

在這個例子中,若 title 屬性上的 unique 規則執行失敗,將不會檢查 max 規則。會依照所指派的順序來執行驗證規則。

有關巢狀屬性的注意事項

若連入的 HTTP Request 中包含「巢狀」的欄位資料,請使用「點 (.)」語法來在驗證規則中指定這些欄位:

1$request->validate([
2 'title' => 'required|unique:posts|max:255',
3 'author.name' => 'required',
4 'author.description' => 'required',
5]);
1$request->validate([
2 'title' => 'required|unique:posts|max:255',
3 'author.name' => 'required',
4 'author.description' => 'required',
5]);

另一方面,若欄位名稱包含 . 字元,則我們可以使用反斜線來逸出句點,以顯式避免被解析成「點 (.)」語法:

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

顯示驗證錯誤

那麼,如果連入 Request 的欄位沒通過給定的驗證規則呢?就像剛才提到過的,Laravel 會自動將使用者重新導向回到上一個位置。此外,所有的驗證規則與 Request 輸入都會自動被快閃存入 Session

Illuminate\View\Middleware\ShareErrorsFromSession Middleware 幫我們在專案中所有的 View 間共享了一個 $errors 變數。這個 Middleware 在 web Middleware 群組中提供。當有套用這個 Middleware 時,所有的 View 中都會有 $errors 變數,因此我們能方便地假設 $errors 變數擁有都已定義好且可安全地使用。$errors 變數是 Illuminate\Support\MessageBag 的實體。更多有關該物件的資訊,請參考 Message Bag 的說明文件

所有,在我們的範例中,當驗證失敗時,使用者會被重新導向到 Controller 的 create 方法,讓我們能在 View 中顯示錯誤訊息:

1<!-- /resources/views/post/create.blade.php -->
2 
3<h1>Create Post</h1>
4 
5@if ($errors->any())
6 <div class="alert alert-danger">
7 <ul>
8 @foreach ($errors->all() as $error)
9 <li>{{ $error }}</li>
10 @endforeach
11 </ul>
12 </div>
13@endif
14 
15<!-- Create Post Form -->
1<!-- /resources/views/post/create.blade.php -->
2 
3<h1>Create Post</h1>
4 
5@if ($errors->any())
6 <div class="alert alert-danger">
7 <ul>
8 @foreach ($errors->all() as $error)
9 <li>{{ $error }}</li>
10 @endforeach
11 </ul>
12 </div>
13@endif
14 
15<!-- Create Post Form -->

自訂錯誤訊息

在專案的 lang/en/validation.php 檔案中,有所有 Laravel 內建驗證規則的錯誤訊息。在這個檔案中,我們可以看到每個驗證規則的翻譯欄位。可以依照需求修改這些訊息。

此外,也可以把這個檔案複製到另一個翻譯語系目錄中,以將其翻成你專案的語言。要瞭解 Laravel 中有關本土化 (Localization) 的更多資訊,請參考完整的本土化說明文件

exclamation

預設情況下,Laravel 專案的 Skeleton 中未包含 lang 目錄。若想自定 Laravel 的語系檔,可以使用 lang:publish Artisan 指令來安裝語系檔:

XHR Request 與驗證

在這個例子中,我們使用傳統的表單來將資料傳給程式。不過,有許多程式是接受來自 JavaScript 前端的 XHR Request。在 XHR Request 中使用 validate 方法時,Laravel 不會產生 Redirect Response,而是產生一個包含所有驗證錯誤的 JSON Response。JSON Response 會以 422 HTTP 狀態碼傳送。

@error 指示詞

可以使用 @error Blade 指示詞來快速判斷給定的屬性是否有驗證錯誤訊息。在 @error 指示詞內,可以輸出 $message 變數來顯示錯誤訊息:

1<!-- /resources/views/post/create.blade.php -->
2 
3<label for="title">Post Title</label>
4 
5<input id="title"
6 type="text"
7 name="title"
8 class="@error('title') is-invalid @enderror">
9 
10@error('title')
11 <div class="alert alert-danger">{{ $message }}</div>
12@enderror
1<!-- /resources/views/post/create.blade.php -->
2 
3<label for="title">Post Title</label>
4 
5<input id="title"
6 type="text"
7 name="title"
8 class="@error('title') is-invalid @enderror">
9 
10@error('title')
11 <div class="alert alert-danger">{{ $message }}</div>
12@enderror

若使用命名的 Error Bag,則可將 Error Bag 的名稱作為第二個引數傳給 @error 指示詞:

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

重新回填表單

當 Laravel 因為驗證錯誤而產生 Redirect Response 時,Laravel 會自動將目前的 Request 輸入快閃存入 Session。這樣一來我們就能在下一個 Request 中方便地存取這些輸入,並將資料重新回填到使用者嘗試送出的表單上。

若要取得前一個 Request 中的快閃輸入,可叫用 Illuminate\Http\Request 上的 old 方法。old 方法從 Session 中拉取前次快閃存入輸入資料:

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

Laravel 也提供了一個全域 old 輔助函式。若想在 Blade 樣板中顯示舊輸入,那麼使用 old 輔助函式來將其填回表單回比較方便。若給定欄位沒有舊輸入的話,會回傳 null

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

有關可選欄位的注意事項

預設情況下,Laravel 的全域 Middleware Stack 中包含了 TrimStringsConvertEmptyStringsToNull Middleware。這兩個 Middleware 由 App\Http\Kernel 類別列在一個 Stack 中。因此,如果不希望 Validator (驗證程式) 把 null 值當作無效資料的話,我們常常需要將「可選填」的 Request 欄位標為 nullable。舉例來說:

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

在這個範例中,我們指定讓 publish_at 欄位可以是 null 或是有效的日期呈現。若沒有在規則定義中加上 nullabale 修飾詞 (Modifier),則 Validator 會把 null 當作無效的日期。

驗證錯誤的 Response 格式

當專案擲回 Illuminate\Validation\ValidationException Exception 且連入 HTTP Request 預期要回傳 JSON Response 時,Laravel 會自動格式化錯誤訊息,並回傳 422 Unprocessable Entity HTTP Response。

下方是一個範例的驗證錯誤 JSON Response 格式。請注意,巢狀的錯誤索引鍵會被扁平化為「點 (.)」表示法:

1{
2 "message": "The team name must be a string. (and 4 more errors)",
3 "errors": {
4 "team_name": [
5 "The team name must be a string.",
6 "The team name must be at least 1 characters."
7 ],
8 "authorization.role": [
9 "The selected authorization.role is invalid."
10 ],
11 "users.0.email": [
12 "The users.0.email field is required."
13 ],
14 "users.2.email": [
15 "The users.2.email must be a valid email address."
16 ]
17 }
18}
1{
2 "message": "The team name must be a string. (and 4 more errors)",
3 "errors": {
4 "team_name": [
5 "The team name must be a string.",
6 "The team name must be at least 1 characters."
7 ],
8 "authorization.role": [
9 "The selected authorization.role is invalid."
10 ],
11 "users.0.email": [
12 "The users.0.email field is required."
13 ],
14 "users.2.email": [
15 "The users.2.email must be a valid email address."
16 ]
17 }
18}

Form Request 的驗證

建立 Form Request

在更複雜的驗證情境中,我們可能會想建立一個「Form Request (表單請求)」。Form Request 就是自訂的 Request 類別,其中封裝了該 Request 自己的驗證與認證邏輯。若要建立 Form Request 類別,可使用 make:request Artisan CLI 指令:

1php artisan make:request StorePostRequest
1php artisan make:request StorePostRequest

產生的 Form Request 會被放在 app/Http/Requests 目錄中。若該目錄不存在,則執行 make:request 指令是會自動建立。Laravel 產生的每個 Form Request 都有兩個方法:authorizerules

讀者可能已經猜到,authorize 方法是用來判斷目前已登入使用者是否能進行該 Request 所代表的動作。rules 方法則回傳要套用到 Request 資料的驗證規則:

1/**
2 * Get the validation rules that apply to the request.
3 *
4 * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
5 */
6public function rules(): array
7{
8 return [
9 'title' => 'required|unique:posts|max:255',
10 'body' => 'required',
11 ];
12}
1/**
2 * Get the validation rules that apply to the request.
3 *
4 * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
5 */
6public function rules(): array
7{
8 return [
9 'title' => 'required|unique:posts|max:255',
10 'body' => 'required',
11 ];
12}
lightbulb

rules 方法的簽章(Signature) 中可以對任何需要的相依性進行型別提示(Type-Hint)。型別提示的相依性會由 Laravel 的 Service Container 自動解析。

那麼,要怎麼執行驗證規則呢?我們只需要在 Controller 方法中型別提示這個 Request 即可。連入的 Form Request 會在呼叫 Controller 方法前驗證。這表示,我們就不需要在 Controller 中放一些凌亂的驗證邏輯:

1/**
2 * Store a new blog post.
3 */
4public function store(StorePostRequest $request): Response
5{
6 // 連入 Request 有效...
7 
8 // 取得已驗證的輸入資料...
9 $validated = $request->validated();
10 
11 // 取得已驗證輸入資料中的一部分...
12 $validated = $request->safe()->only(['name', 'email']);
13 $validated = $request->safe()->except(['name', 'email']);
14 
15 // 保存部落格貼文...
16 
17 return response()->noContent();
18}
1/**
2 * Store a new blog post.
3 */
4public function store(StorePostRequest $request): Response
5{
6 // 連入 Request 有效...
7 
8 // 取得已驗證的輸入資料...
9 $validated = $request->validated();
10 
11 // 取得已驗證輸入資料中的一部分...
12 $validated = $request->safe()->only(['name', 'email']);
13 $validated = $request->safe()->except(['name', 'email']);
14 
15 // 保存部落格貼文...
16 
17 return response()->noContent();
18}

若驗證失敗,會產生一個 Redirect Response,並將使用者傳送回前一個位置。錯誤訊息也會被快閃存入 Session 中以便顯示。若目前的 Request 是 XHR Request,則會回傳一個 422 狀態碼的 HTTP Response 給使用者,其中包含了以 JSON 呈現的驗證錯誤訊息

新增 After Hook 到 Form Request

若想將「After」驗證 Hook 加到 Form Request 上,則需要使用 withValidator 方法。該方法接收完整建構好的 Validator,能讓你在實際執行驗證規則前呼叫 Validator 上的任何方法:

1use Illuminate\Validation\Validator;
2 
3/**
4 * Configure the validator instance.
5 */
6public function withValidator(Validator $validator): void
7{
8 $validator->after(function (Validator $validator) {
9 if ($this->somethingElseIsInvalid()) {
10 $validator->errors()->add('field', 'Something is wrong with this field!');
11 }
12 });
13}
1use Illuminate\Validation\Validator;
2 
3/**
4 * Configure the validator instance.
5 */
6public function withValidator(Validator $validator): void
7{
8 $validator->after(function (Validator $validator) {
9 if ($this->somethingElseIsInvalid()) {
10 $validator->errors()->add('field', 'Something is wrong with this field!');
11 }
12 });
13}

在第一個屬性驗證失敗後就停止

在 Request 類別上新增 stopOnFirstFailure 屬性後,就可以讓 Validator 在發生一個驗證失敗後就停止驗證所有的屬性:

1/**
2 * Indicates if the validator should stop on the first rule failure.
3 *
4 * @var bool
5 */
6protected $stopOnFirstFailure = true;
1/**
2 * Indicates if the validator should stop on the first rule failure.
3 *
4 * @var bool
5 */
6protected $stopOnFirstFailure = true;

自訂重新導向位置

前面也提到過,Form Request 驗證失敗時會產生一個 Redirect Response 來將使用者傳送到前一個位置。不過,我們可以自訂這個行為。為此,請在 Form Request 中定義一個 $redirect 屬性:

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

或者,若想將使用者重新導向到命名 Route,請改定義 $redirectRoute 屬性:

1/**
2 * The route that users should be redirected to if validation fails.
3 *
4 * @var string
5 */
6protected $redirectRoute = 'dashboard';
1/**
2 * The route that users should be redirected to if validation fails.
3 *
4 * @var string
5 */
6protected $redirectRoute = 'dashboard';

授權 Form Request

Form Request 類別中也包含了一個 authorize 方法。在這個方法中,我們可以判斷已登入使用者是否有授權能更新給定資源。舉例來說,我們可以判斷使用者是否真的擁有正在編輯的部落格留言。大多數情況下,在這個方法中我們應該都是使用授權的 Gate 與 Policy

1use App\Models\Comment;
2 
3/**
4 * Determine if the user is authorized to make this request.
5 */
6public function authorize(): bool
7{
8 $comment = Comment::find($this->route('comment'));
9 
10 return $comment && $this->user()->can('update', $comment);
11}
1use App\Models\Comment;
2 
3/**
4 * Determine if the user is authorized to make this request.
5 */
6public function authorize(): bool
7{
8 $comment = Comment::find($this->route('comment'));
9 
10 return $comment && $this->user()->can('update', $comment);
11}

由於所有 Form Request 都繼承自 Laravel 的基礎 Request 類別,因此我們可以使用 user 方法來存取目前已登入的使用者。此外,也請注意上方範例中呼叫的 route 方法。這個方法能讓我們存取目前呼叫的 Route 上的 URI 參數,如上述例子中為 {comment} 參數:

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

因此,若我們的專案有使用 Route Model 繫結,則這裡的程式碼還能存取 Request 上已解析的 Model 屬性來進一步簡化:

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

authorize 方法回傳 false,則會自動回傳一個 403 狀態碼的 HTTP Respnose,而 Controller 則不會被執行。

若想在程式中的其他部分處理授權邏輯,只要在 authorize 方法中回傳 true 即可:

1/**
2 * Determine if the user is authorized to make this request.
3 */
4public function authorize(): bool
5{
6 return true;
7}
1/**
2 * Determine if the user is authorized to make this request.
3 */
4public function authorize(): bool
5{
6 return true;
7}
lightbulb

authorize 方法的簽章(Signature) 中可以對任何需要的相依性進行型別提示(Type-Hint)。型別提示中的相依性會由 Laravel 的 Service Container 自動解析。

自訂錯誤訊息

可以複寫 messages 方法來自訂 Form Request 使用的錯誤訊息。這個方法應回傳一組包含屬性/ 規則配對的陣列與其對應的錯誤訊息:

1/**
2 * Get the error messages for the defined validation rules.
3 *
4 * @return array<string, string>
5 */
6public function messages(): array
7{
8 return [
9 'title.required' => 'A title is required',
10 'body.required' => 'A message is required',
11 ];
12}
1/**
2 * Get the error messages for the defined validation rules.
3 *
4 * @return array<string, string>
5 */
6public function messages(): array
7{
8 return [
9 'title.required' => 'A title is required',
10 'body.required' => 'A message is required',
11 ];
12}

自訂驗證屬性

Laravel 中許多的內建驗證規則錯誤訊息都包含了一個 :attribute 預留位置 (Placeholder)。若想將驗證訊息中 :attribute 預留位置該為自訂屬性名稱,可以複寫 attributes 方法來指定自訂的名稱。這個方法應回傳一組包含屬性 / 名稱配對的陣列:

1/**
2 * Get custom attributes for validator errors.
3 *
4 * @return array<string, string>
5 */
6public function attributes(): array
7{
8 return [
9 'email' => 'email address',
10 ];
11}
1/**
2 * Get custom attributes for validator errors.
3 *
4 * @return array<string, string>
5 */
6public function attributes(): array
7{
8 return [
9 'email' => 'email address',
10 ];
11}

為驗證準備輸入

若有需要在套用驗證規則前準備或消毒 (Sanitize) 任何 Request 中的資料,可使用 prepareForValidation 方法:

1use Illuminate\Support\Str;
2 
3/**
4 * Prepare the data for validation.
5 */
6protected function prepareForValidation(): void
7{
8 $this->merge([
9 'slug' => Str::slug($this->slug),
10 ]);
11}
1use Illuminate\Support\Str;
2 
3/**
4 * Prepare the data for validation.
5 */
6protected function prepareForValidation(): void
7{
8 $this->merge([
9 'slug' => Str::slug($this->slug),
10 ]);
11}

類似地,若有需要在驗證完成後正常化任何 Request 資料,可使用 passedValidation 方法:

1use Illuminate\Support\Str;
2 
3/**
4 * Handle a passed validation attempt.
5 */
6protected function passedValidation(): void
7{
8 $this->replace(['name' => 'Taylor']);
9}
1use Illuminate\Support\Str;
2 
3/**
4 * Handle a passed validation attempt.
5 */
6protected function passedValidation(): void
7{
8 $this->replace(['name' => 'Taylor']);
9}

手動建立 Validator

若不想使用 Request 上的 validate 方法,也可以使用 Validator Facade 來手動建立 Validator 實體。Facade 方法上的 make 方法會產生新的 Validator 實體:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use Illuminate\Http\Request;
7use Illuminate\Http\Response;
8use Illuminate\Support\Facades\Validator;
9 
10class PostController extends Controller
11{
12 /**
13 * Store a new blog post.
14 */
15 public function store(Request $request): Response
16 {
17 $validator = Validator::make($request->all(), [
18 'title' => 'required|unique:posts|max:255',
19 'body' => 'required',
20 ]);
21 
22 if ($validator->fails()) {
23 return redirect('post/create')
24 ->withErrors($validator)
25 ->withInput();
26 }
27 
28 // 取得已驗證的輸入...
29 $validated = $validator->validated();
30 
31 // 取得已驗證輸入的一部分...
32 $validated = $validator->safe()->only(['name', 'email']);
33 $validated = $validator->safe()->except(['name', 'email']);
34 
35 // 保存部落格貼文...
36 
37 return response()->noContent();
38 }
39}
1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use Illuminate\Http\Request;
7use Illuminate\Http\Response;
8use Illuminate\Support\Facades\Validator;
9 
10class PostController extends Controller
11{
12 /**
13 * Store a new blog post.
14 */
15 public function store(Request $request): Response
16 {
17 $validator = Validator::make($request->all(), [
18 'title' => 'required|unique:posts|max:255',
19 'body' => 'required',
20 ]);
21 
22 if ($validator->fails()) {
23 return redirect('post/create')
24 ->withErrors($validator)
25 ->withInput();
26 }
27 
28 // 取得已驗證的輸入...
29 $validated = $validator->validated();
30 
31 // 取得已驗證輸入的一部分...
32 $validated = $validator->safe()->only(['name', 'email']);
33 $validated = $validator->safe()->except(['name', 'email']);
34 
35 // 保存部落格貼文...
36 
37 return response()->noContent();
38 }
39}

傳入 make 方法的第一個屬性是要驗證的資料。第二個引述則是一組要套用到給定資料上的驗證規則陣列。

在判斷 Request 是否驗證失敗後,可以使用 withErrors 方法來將錯誤訊息快閃存入 Session 中。使用這個方法時,重新導向後會自動共享 $errors 變數,讓我們能輕鬆將其顯示給使用者。withErrors 方法接受一個 Validator、MessageBag、或 PHP array

在第一個驗證失敗後就停止

stopOnFirstFailure 方法可以讓 Validator 在發生一個驗證失敗後就停止驗證所有的屬性:

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

自動重新導向

若想手動建立 Validator 實體,但也想要使用 HTTP Request 的 validate 方法提供的自動重新導向功能,可以在現有 Validator 實體上呼叫 validate 方法。若驗證失敗,使用者會被重新導向。XHR Request 的情況下,則會回傳 JSON Response

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

可以使用 validateWithBag 方法來在驗證失敗時將錯誤訊息保存在命名的 Error Bag 中:

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

命名的 Error Bag

若單一頁面中有多個表單,則我們可能會想為保存錯誤訊息的 MessageBag 命名。這樣一來,我們就可以為特定的表單取得錯誤訊息。為此,請傳入名稱作為第二個引數給 withErrors

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

接著我們就可以在 $errors 變數中存取命名的 MessageBag 實體:

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

自訂錯誤訊息

當然,除了 Laravel 提供的預設錯誤訊息外,我們還可以提供自訂的錯誤訊息給 Validator 實體使用。有許多方法可以指定自訂訊息。第一個方法是,將自訂訊息作為第三個引數傳給 Validator::make 方法:

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

在這個例子中,:attribute 預留位置 (Placeholder) 會被替換成驗證中的實際欄位名稱。我們也可以在驗證訊息中使用其他的預留位置,如:

1$messages = [
2 'same' => 'The :attribute and :other must match.',
3 'size' => 'The :attribute must be exactly :size.',
4 'between' => 'The :attribute value :input is not between :min - :max.',
5 'in' => 'The :attribute must be one of the following types: :values',
6];
1$messages = [
2 'same' => 'The :attribute and :other must match.',
3 'size' => 'The :attribute must be exactly :size.',
4 'between' => 'The :attribute value :input is not between :min - :max.',
5 'in' => 'The :attribute must be one of the following types: :values',
6];

為給定屬性指定自訂訊息

有時候我們可能會想指為特定的屬性指定錯誤訊息。為此,我們可以使用「點 (.)」標記法。先指定屬性的名稱,然後再加上規則名稱:

1$messages = [
2 'email.required' => 'We need to know your email address!',
3];
1$messages = [
2 'email.required' => 'We need to know your email address!',
3];

指定自訂屬性值

Laravel 中許多內建的錯誤訊息都包含了一個 :attribute 預留位置,會被取代成正在驗證的欄位名稱或屬性名稱。若想在指定的欄位上自使用自訂值來取代這些預留位置,可將一組自訂屬性的陣列作為第四個引數傳給 Validator::make 方法:

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

驗證的「After」Hook

我們可以附加一個要在驗證完成後才執行的回呼。這樣一來,我們就可以輕鬆地做進一步的驗證、甚至是將更多的錯誤訊息加到 Message Collection 上。要開始加上 After Hook,請在 Validator 實體上呼叫 after 方法:

1use Illuminate\Support\Facades;
2use Illuminate\Validation\Validator;
3 
4$validator = Facades\Validator::make(/* ... */);
5 
6$validator->after(function (Validator $validator) {
7 if ($this->somethingElseIsInvalid()) {
8 $validator->errors()->add(
9 'field', 'Something is wrong with this field!'
10 );
11 }
12});
13 
14if ($validator->fails()) {
15 // ...
16}
1use Illuminate\Support\Facades;
2use Illuminate\Validation\Validator;
3 
4$validator = Facades\Validator::make(/* ... */);
5 
6$validator->after(function (Validator $validator) {
7 if ($this->somethingElseIsInvalid()) {
8 $validator->errors()->add(
9 'field', 'Something is wrong with this field!'
10 );
11 }
12});
13 
14if ($validator->fails()) {
15 // ...
16}

處理已驗證的輸入

使用 Form Request 或手動建立的 Validator 實體驗證好連入的 Request 資料後,我們可能會想取得連入 Request 中實際被驗證過的資料。有許多種方法可以取得這些資料。第一種方法是在 Form Request 或 Validator 實體上呼叫 validated 方法。這個方法會回傳一組驗證過的資料陣列:

1$validated = $request->validated();
2 
3$validated = $validator->validated();
1$validated = $request->validated();
2 
3$validated = $validator->validated();

或者,也可以在 Form Request 或 Validator 實體上呼叫 safe 方法。這個方法會回傳一個 Illuminate\Support\ValidatedInput 實體。該物件提供了 onlyexceptall 等方法,可用來取得一部分已驗證的資料或是整個已驗證資料的陣列:

1$validated = $request->safe()->only(['name', 'email']);
2 
3$validated = $request->safe()->except(['name', 'email']);
4 
5$validated = $request->safe()->all();
1$validated = $request->safe()->only(['name', 'email']);
2 
3$validated = $request->safe()->except(['name', 'email']);
4 
5$validated = $request->safe()->all();

此外,也可迭代 Illuminate\Support\ValidatedInput 或像陣列一樣存取:

1// 可迭代已驗證資料...
2foreach ($request->safe() as $key => $value) {
3 // ...
4}
5 
6// 可將已驗證資料作為陣列存取...
7$validated = $request->safe();
8 
9$email = $validated['email'];
1// 可迭代已驗證資料...
2foreach ($request->safe() as $key => $value) {
3 // ...
4}
5 
6// 可將已驗證資料作為陣列存取...
7$validated = $request->safe();
8 
9$email = $validated['email'];

若想在已驗證資料上加上額外的欄位,可呼叫 merge 方法:

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

若想將已驗證資料作為 Collection 實體取得,可呼叫 collect 方法:

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

處理錯誤訊息

Validator 實體上呼叫 errors 方法後,會收到 Illuminate\Support\MessageBag 實體。該實體提供了多種方便的方法能讓我們處理錯誤訊息。自動提供給所有 View 的 $errors 變數也是一個 MessageBag 類別的實體。

取得某個欄位的第一筆錯誤訊息

若要取得給定欄位的第一筆錯誤訊息,請使用 first 方法:

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

取得某個欄位的所有錯誤訊息

若需要取得給定欄位的所有訊息陣列,請使用 get 方法:

1foreach ($errors->get('email') as $message) {
2 // ...
3}
1foreach ($errors->get('email') as $message) {
2 // ...
3}

在驗證某個陣列格式的表單欄位時,可使用 * 字元來取得各個陣列元素的所有錯誤訊息:

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

取得全部欄位的所有訊息

若要取得所有欄位的所有訊息陣列,請使用 all 方法:

1foreach ($errors->all() as $message) {
2 // ...
3}
1foreach ($errors->all() as $message) {
2 // ...
3}

判斷某個欄位是否有錯誤訊息

has 方法可用來判斷給定的欄位是否有錯誤訊息:

1if ($errors->has('email')) {
2 // ...
3}
1if ($errors->has('email')) {
2 // ...
3}

在語系檔中指定自訂訊息

在專案的 lang/en/validation.php 檔案中,有所有 Laravel 內建驗證規則的錯誤訊息。在這個檔案中,我們可以看到每個驗證規則的翻譯欄位。可以依照需求修改這些訊息。

此外,也可以把這個檔案複製到另一個翻譯語系目錄中,以將其翻成你專案的語言。要瞭解 Laravel 中有關本土化 (Localization) 的更多資訊,請參考完整的本土化說明文件

exclamation

預設情況下,Laravel 專案的 Skeleton 中未包含 lang 目錄。若想自定 Laravel 的語系檔,可以使用 lang:publish Artisan 指令來安裝語系檔:

為特定屬性指定自訂訊息

我們可能會想在程式的驗證語系檔中為特定的屬性與規則組合自訂錯誤訊息。為此,請在專案的 lang/xx/validation.php 語系檔中 custom 陣列內新增你的自訂訊息:

1'custom' => [
2 'email' => [
3 'required' => 'We need to know your email address!',
4 'max' => 'Your email address is too long!'
5 ],
6],
1'custom' => [
2 'email' => [
3 'required' => 'We need to know your email address!',
4 'max' => 'Your email address is too long!'
5 ],
6],

在語系檔中指定屬性

Laravel 中內建的許多錯誤訊息都包含了一個 :attribute 預留位置 (Placeholder),該預留位置會被取代為被驗證的欄位名稱或屬性名稱。若想讓驗證訊息的 :attribute 部分被取代為自訂的值,可在 lang/xx/validation.php 語系檔中 attributes 陣列內指定自訂的屬性名稱:

1'attributes' => [
2 'email' => 'email address',
3],
1'attributes' => [
2 'email' => 'email address',
3],
exclamation

預設情況下,Laravel 專案的 Skeleton 中未包含 lang 目錄。若想自定 Laravel 的語系檔,可以使用 lang:publish Artisan 指令來安裝語系檔:

在語系檔中指定值

Laravel 中有些內建的驗證規則錯誤訊息中包含了一個 :value 預留位置 (Placeholder),這個預留位置會被取代為目前 Request 中的屬性值。不過,有時候我們會像讓驗證訊息中的 :value 部分被取代為用於該值的自訂呈現方式。舉例來說,假設我們套用了下列規則來讓 payment_type 值為 cc 時,信用卡卡號為必填:

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

若驗證規則執行失敗,會產生下列錯誤訊息:

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

我們可以在 lang/xx/validation.php 語系檔中定義一個 values 陣列來為付款方式的值指定一個對使用者更友好的呈現,而不是顯示 cc

1'values' => [
2 'payment_type' => [
3 'cc' => 'credit card'
4 ],
5],
1'values' => [
2 'payment_type' => [
3 'cc' => 'credit card'
4 ],
5],
exclamation

預設情況下,Laravel 專案的 Skeleton 中未包含 lang 目錄。若想自定 Laravel 的語系檔,可以使用 lang:publish Artisan 指令來安裝語系檔:

定義好這個值之後,剛才的驗證規則會產生下列錯誤訊息:

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

可用的驗證規則

下面列出了所有可用的驗證規則與其函式:

accepted

驗證欄位必須為 "yes""on"1true 等。適用於驗證類似是否已接受「服務條款」等欄位。

accepted_if:anotherfield,value,...

若另一個驗證欄位符合給定的值,則該驗證欄位必須為 "yes""on"1true。適用於驗證類似是否接受「服務條款」等欄位。

active_url

該驗證欄位在使用 dns_get_record PHP 函式時必須有有效的 A 紀錄或 AAAA 紀錄。在傳送給 dns_get_record 前,主機名稱是從提供的 URL 中使用 parse_url PHP 方法取出的。

after:日期

該驗證欄位必須為給定日期後的日期。日期會使用 PHP 的 strtotime 函式來轉換為有效的 DataTime 實體:

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

除了將日期字串直接傳入 strtotime 取值外,也可以指定另一個欄位來比較日期:

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

after_or_equal:日期

該驗證欄位的值必須在給定日期之後或等於給定日期。更多資訊請參考 after 規則。

alpha

該驗證欄位必須只由 \p{L}\p{M} 內的 Unicode 的字母字元組成。

若要進一步限制該驗證規則為只允許 ASCII 範圍 (a-zA-Z),可提供 ascii 選項給該驗證規則:

1'username' => 'alpha:ascii',
1'username' => 'alpha:ascii',

alpha_dash

該驗證欄位必須完全由 \p{L}](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AL%3A%5D&g=&i=)、\p{M}\p{N} 內所包含的 Unicode 字母數字字元、以及 ASCII 的減號 (-) 與 ASCII 的底線 (_) 所組成。

若要進一步限制該驗證規則為只允許 ASCII 範圍 (a-zA-Z),可提供 ascii 選項給該驗證規則:

1'username' => 'alpha_dash:ascii',
1'username' => 'alpha_dash:ascii',

alpha_num

該驗證欄位必須完全由 \p{L}](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5B%3AL%3A%5D&g=&i=)、\p{M}\p{N} 內所包含的 Unicode 字母數字字元所組成。

若要進一步限制該驗證規則為只允許 ASCII 範圍 (a-zA-Z),可提供 ascii 選項給該驗證規則:

1'username' => 'alpha_num:ascii',
1'username' => 'alpha_num:ascii',

array

該欄位必須為一 PHP array

若有提供額外的值給 array 規則,則輸入陣列中的每個索引鍵都必須要在提供給該規則的列表值中。在下列的例子中,admin 索引鍵是無效的,因為 admin 不包含在我們提供給 array 規則的數值列表中:

1use Illuminate\Support\Facades\Validator;
2 
3$input = [
4 'user' => [
5 'name' => 'Taylor Otwell',
6 'username' => 'taylorotwell',
7 'admin' => true,
8 ],
9];
10 
11Validator::make($input, [
12 'user' => 'array:name,username',
13]);
1use Illuminate\Support\Facades\Validator;
2 
3$input = [
4 'user' => [
5 'name' => 'Taylor Otwell',
6 'username' => 'taylorotwell',
7 'admin' => true,
8 ],
9];
10 
11Validator::make($input, [
12 'user' => 'array:name,username',
13]);

一般來說,請總是指定允許出現在陣列中的索引鍵:

ascii

該驗證欄位只能由 7 位元的 ASCII 字元組成。

bail

該欄位中某項驗證規則失敗後,停止驗證該欄位的其他規則。

bail 規則會在遇到驗證失敗時停止驗證該欄位,而 stopOnFirstFailure 方法則會讓 Validator 在遇到一個驗證失敗的時候就停止所有屬性的驗證:

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

before:日期

驗證欄位必須為給定日期之前的日期。該日期會被傳給 PHP 的 strtotime 函式,以轉換為有效的 DateTime 實體。此外,與 after 規則一樣,我們也可以提供驗證欄位中的另一個欄位來作為 日期 的值。

before_or_equal:日期

驗證欄位必須為給定日期或給定日期之前的日期。該日期會被傳給 PHP 的 strtotime 函式,以轉換為有效的 DateTime 實體。此外,與 after 規則一樣,我們也可以提供驗證欄位中的另一個欄位來作為 日期 的值。

between:最小值,最大值

該驗證欄位的大小必須介於給定的 最小值最大值 之間 (含)。字串、數字、陣列、與檔案會使用與 size 規則相同的方法計算大小。

boolean

該驗證欄位必須能被轉為布林值。可接受的輸入為 true, false, 1, 0, "1", 與 "0"

confirmed

該驗證欄位必須與 {欄位}_confirmation 相符合。舉例來說,若正在驗證的欄位是 password,則輸入中必須有相符的 password_confirmation 欄位。

current_password

驗證欄位必須符合目前登入使用者的密碼。可以使用規則的第一個參數來指定認證 Guard

1'password' => 'current_password:api'
1'password' => 'current_password:api'

date

驗證欄位在依照 strtotime PHP 函式時,必須是有效且非相對的日期。

date_equals:日期

該驗證欄位必須為給定日期或給定日期後的日期。日期會使用 PHP 的 strtotime 函式來轉換為有效的 DataTime 實體:

date_format:格式,...

驗證欄位必須符合其中一個給定的 格式。驗證欄位時只能使用 datedate_format 擇一,不可同時使用。該驗證規則支援 PHP DateTime 類別支援的所有格式。

decimal:最小值,最大值

該驗證欄位必須為數字 (Numeric),且必須包含特定位數的小數點:

1// 必須正好有兩位小數點 (9.99)...
2'price' => 'decimal:2'
3 
4// 必須有介於 2 到 4 位小數點位數...
5'price' => 'decimal:2,4'
1// 必須正好有兩位小數點 (9.99)...
2'price' => 'decimal:2'
3 
4// 必須有介於 2 到 4 位小數點位數...
5'price' => 'decimal:2,4'

declined

該驗證欄位必須為 "no", "off", 0, 或 false

declined_if:另一個欄位,值,...

若驗證中另一個欄位符合給定的值時,該驗證欄位必須為 "no", "off", 0, 或 false

different:欄位

該驗證欄位必須與 欄位 的值不同。

digits:

要驗證的整數的長度必須完全符合

digits_between:最小值,最大值

要驗證的整數長度必須介於給定的 最小值最大值

dimensions

該驗證欄位必須為一張圖片,且必須符合規則參數所指定的長寬限制:

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

可用的條件限制為:最小寬度 min_width、最大寬度 max_width、最小高度 min_height、最大高度 max_height、寬度 width、高度 height、長寬比 ratio

長寬比 ratio 以寬除以高來呈現。可以使用如 3/2 這樣的分數,或是如 1.5 這樣的浮點數來表示:

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

由於這個規則要求多個引數,所以也可以使用 Rule::dimensions 方法來流暢地建立規則:

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($data, [
5 'avatar' => [
6 'required',
7 Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2),
8 ],
9]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($data, [
5 'avatar' => [
6 'required',
7 Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2),
8 ],
9]);

distinct

在驗證陣列時,該驗證欄位必須不含重複的值:

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

Distinct 預設使用鬆散的 (Loose) 變數比較。若要使用嚴格 (Strict) 比較,可在驗證規則定義中加上 strict 參數:

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

可以將 ignore_case 加到驗證規則的參數內來讓該規則忽略大小寫差異:

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

doesnt_start_with:foo,bar,...

該驗證欄位不可以任何給定的值開頭。

doesnt_end_with:foo,bar,...

該驗證欄位不可以任何給定的值結尾。

email

驗證欄位必須為 E-Mail 位址格式。該驗證規則使用 egulias/email-validator 套件來驗證 E-Mail位址。預設情況下,使用 RFCValidation Validator,不過,也可以自訂套用其他驗證風格:

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

上方的例子會套用 RFCValidationDNSCheckValidation 驗證。此處列出了所有可套用的驗證風格:

  • rfc: RFCValidation
  • strict: NoRFCWarningsValidation
  • dns: DNSCheckValidation
  • spoof: SpoofCheckValidation
  • filter: FilterEmailValidation
  • filter_unicode: FilterEmailValidation::unicode()

filter Validator 使用 PHP 的 filter_var 函式,是隨 Laravel 提供的 Validator。在 Laravel 5.8 以前是 Laravel 的預設 E-Mail 驗證行為。

exclamation

dnsspoof Validator 需要有 PHP 的 intl 擴充程式。

ends_with:foo,bar,...

該驗證欄位必須以其中一個給定的值結尾。

enum

Enum 規則是一個基於類別的規則,會驗證該驗證欄位是否包含有效的 Enum 值。Enum 規則接受一個 Enum 的名稱作為其唯一的 Constructor (建構函式) 引數:

1use App\Enums\ServerStatus;
2use Illuminate\Validation\Rules\Enum;
3 
4$request->validate([
5 'status' => [new Enum(ServerStatus::class)],
6]);
1use App\Enums\ServerStatus;
2use Illuminate\Validation\Rules\Enum;
3 
4$request->validate([
5 'status' => [new Enum(ServerStatus::class)],
6]);
exclamation

Enum 只在 PHP 8.1 以上提供。

exclude

validatevalidated 方法回傳的 Request 資料中會排除此驗證欄位。

exclude_if:另一欄位,

另一欄位 欄位的值是 ,則 validatevalidated 方法回傳的 Request 資料中會排除此驗證欄位。

若有需要使用複雜的邏輯條件來排除欄位,可使用 Rule::excludeIf 方法。該方法接受一個布林值或閉包。傳入閉包時,該閉包應回傳 truefalse,來判斷該驗證欄位是否要被排除:

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($request->all(), [
5 'role_id' => Rule::excludeIf($request->user()->is_admin),
6]);
7 
8Validator::make($request->all(), [
9 'role_id' => Rule::excludeIf(fn () => $request->user()->is_admin),
10]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($request->all(), [
5 'role_id' => Rule::excludeIf($request->user()->is_admin),
6]);
7 
8Validator::make($request->all(), [
9 'role_id' => Rule::excludeIf(fn () => $request->user()->is_admin),
10]);

exclude_unless:另一欄位,

除非 另一欄位,否則 validatevalidated 方法回傳的 Request 資料中將不會排除該驗證欄位。若 null (exclude_unless:name,null),則除非要比較的欄位為 null 或 Request 資料中沒有要比較的欄位,否則該驗證欄位將不會被排除。

exclude_with:anotherfield

另一欄位 存在,則 validatevalidated 方法回傳的 Request 資料中將排除該驗證欄位。

exclude_without:另一欄位

另一欄位 不存在,則 validatevalidated 方法回傳的 Request 資料中將排除該驗證欄位。

exists:資料表,欄位

該驗證欄位必須在給定資料庫資料表中存在。

Exists 規則的基本用法

1'state' => 'exists:states'
1'state' => 'exists:states'

若未指定 column 欄位,則會該驗證欄位的名稱。因此,在這個例子中,本規則會驗證 states 資料表中是否包含有一筆 state 欄位值符合 Request 中 state 屬性值的紀錄。

指定自訂欄位名稱

也可以顯式指定本驗證規則要使用的資料庫欄位名稱。只需要將欄位名稱放在資料表名稱後即可:

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

有時候,我們可能會需要指定 exists 查詢使用的資料庫連線。為此,我們只要在資料表名稱前方加上連線名稱即可:

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

除了直接指定資料表名稱外,也可以指定要用來判斷資料表名稱的 Eloquent Model:

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

若想自訂該驗證規則執行的查詢,可以使用 Rule 類別來流暢地定義該規則。在這個範例中,我們還會使用陣列來指定驗證規則,而不是使用 | 字元來區分各個規則:

1use Illuminate\Database\Query\Builder;
2use Illuminate\Support\Facades\Validator;
3use Illuminate\Validation\Rule;
4 
5Validator::make($data, [
6 'email' => [
7 'required',
8 Rule::exists('staff')->where(function (Builder $query) {
9 return $query->where('account_id', 1);
10 }),
11 ],
12]);
1use Illuminate\Database\Query\Builder;
2use Illuminate\Support\Facades\Validator;
3use Illuminate\Validation\Rule;
4 
5Validator::make($data, [
6 'email' => [
7 'required',
8 Rule::exists('staff')->where(function (Builder $query) {
9 return $query->where('account_id', 1);
10 }),
11 ],
12]);

只要在 exists 方法的第二個引數上提供欄位名稱,就可以明顯指定 Rule::exists 方法所產生的 exists 規則要使用的資料庫欄位名稱:

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

file

該驗證欄位必須為一成功上傳的檔案。

filled

當該驗證欄位存在時,不可為空。

gt:欄位

該驗證欄位必須大於給定的 欄位。這兩個欄位必須為相同型別。字串、數字、陣列、檔案等,都使用與 size 規則相同的方式計算大小。

gte:欄位

該驗證欄位必須大於或等於給定的 欄位。這兩個欄位必須為相同型別。字串、數字、陣列、檔案等,都使用與 size 規則相同的方式計算大小。

image

該驗證欄位必須為一圖片 (jpg, jpeg, png, bmp, gif, svg, 或 webp)。

in:foo,bar,...

該驗證欄位必須要包含在給定的列表值中。使用這個規則時,我們常常需要對陣列 implode,所以我們還能使用 Rule::in 方法來流暢地建立該規則:

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($data, [
5 'zones' => [
6 'required',
7 Rule::in(['first-zone', 'second-zone']),
8 ],
9]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($data, [
5 'zones' => [
6 'required',
7 Rule::in(['first-zone', 'second-zone']),
8 ],
9]);

若與 array 規則一起使用 in 規則,則輸入陣列中的每個值都必須要包含在提供給 in 規則的列表值中。在下面的例子中,輸入陣列內的 LAS 機場代碼是無效的,因為提供給 in 規則的機場列表中未包含 LAS

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4$input = [
5 'airports' => ['NYC', 'LAS'],
6];
7 
8Validator::make($input, [
9 'airports' => [
10 'required',
11 'array',
12 ],
13 'airports.*' => Rule::in(['NYC', 'LIT']),
14]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4$input = [
5 'airports' => ['NYC', 'LAS'],
6];
7 
8Validator::make($input, [
9 'airports' => [
10 'required',
11 'array',
12 ],
13 'airports.*' => Rule::in(['NYC', 'LIT']),
14]);

in_array:另一欄位.*

該驗證欄位的值必須存在於 另一欄位 的值中。

integer

該驗證欄位必須為整數。

exclamation

這個驗證規則並不會驗證輸入是否為「整數(Integer)」變數型別,只會驗證該輸入值是否為 PHP 的 FILTER_VALIDATE_INT 規則接受的類型。若想驗證輸入是否為一數字,請搭配 numeric 驗證規則一起使用此規則。

ip

該驗證欄位必須為一 IP 位址。

ipv4

該驗證欄位必須為一 IPv4 位址。

ipv6

該驗證欄位必須為一 IPv6 位址。

json

該驗證欄位必須為有效的 JSON 字串。

lt:欄位

該驗證欄位必須小於給定的 欄位。這兩個欄位必須為相同型別。字串、數字、陣列、檔案等,將使用與 size 規則相同的方式計算長度。

lte:欄位

該驗證欄位必須小於或等於給定的 欄位。這兩個欄位必須為相同型別。字串、數字、陣列、檔案等,將使用與 size 規則相同的方式計算長度。

lowercase

該驗證欄位必須為小寫字母。

mac_address

該驗證欄位必須為一 MAC 位址。

max:

該驗證欄位必須小於或等於最大值 。字串、數字、陣列、檔案等會使用與 size 規則相同的方法計算大小。

max_digits:

要驗證的整數位數必須小於

mimetypes:text/plain,...

該驗證欄位的檔案必須為其中一個給定的 MIME 型別:

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

若要判斷上傳檔案的 MIME 類型,Laravel 會讀取該檔案的內容,並嘗試推測 MIME 類型。推測的 MIME 類型可能會與用戶端提供的 MIME 類型不同。

mimes:foo,bar,...

該驗證欄位的檔案必須為列出的副檔名中其中一個對應的 MIME 類型。

MIME 規則的基礎用法

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

雖然我們只需要指定副檔名,不過這個規則會讀取該檔案的內容並判斷 MIME 類型,再實際去驗證 MIME 類型。可以在下列位置找到一組 MIME 類型與其對應副檔名的列表:

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

min:

該驗證欄位必須有最小值 最小值。字串、數字、陣列、與檔案會使用與 size 規則相同的方法計算大小。

min_digits:

要驗證的整數位數必須大於

multiple_of:

該驗證欄位必須為 的倍數。

missing

要驗證的欄位不可存在於輸入資料中。

missing_if:另一欄位,,...

另一欄位 欄位為給定的其中一個 時,該驗證欄位不可存在。

missing_unless:另一欄位,

除非 另一欄位 欄位為給定的其中一個 時,否則該驗證欄位不可存在。

missing_with:foo,bar,...

只有在 任一指定的其他欄位存在時,該驗證欄位不可存在。

missing_with_all:foo,bar,...

只有在 所有指定的其他欄位都存在時,該驗證欄位不可存在。

not_in:foo,bar,...

該驗證欄位不可包含在給定的列表值中。可使用 Rule::notIn 方法來流暢地建立此規則:

1use Illuminate\Validation\Rule;
2 
3Validator::make($data, [
4 'toppings' => [
5 'required',
6 Rule::notIn(['sprinkles', 'cherries']),
7 ],
8]);
1use Illuminate\Validation\Rule;
2 
3Validator::make($data, [
4 'toppings' => [
5 'required',
6 Rule::notIn(['sprinkles', 'cherries']),
7 ],
8]);

not_regex:格式

該驗證欄位不可符合給定的正規表示式 (Regular Expression)。

在這個規則內部,使用了 PHP 的 preg_match 函式。指定的規則必須符合 preg_match 所要求的格式,因此也必須包含有效的分隔字元(Delimiter)。例如:'email' => 'not_regex:/^.+$/i'

exclamation

在使用 regex / not_regex 格式時,可能會需要以變數方式來指定驗證規則,而不是使用 | 分隔符號。尤其是當正規表示式包含 | 字元時。

nullable

該驗證欄位可為 null。。

numeric

該驗證欄位必須為數字 (Numeric)

password

該驗證欄位必須符合已登入使用者的密碼。

exclamation

該驗證欄位已改名為 current_password,並將於 Laravel 9 中移除。請改用 current_password 規則代替。

present

要驗證的欄位必須存在於輸入資料中。

prohibited

要驗證的欄位必須不存在或為空。當欄位符合下列條件時,將視該欄位為空:

  • 該值為 null
  • 該值為空字串。
  • 該值為空陣列或空的 Countable 物件。
  • 該值為已上傳的檔案,並且路徑為空。

prohibited_if:另一欄位,,...

另一欄位 相符與任意的 ,則要驗證的欄位必須不存在或為空。當欄位滿足下列條件時,將視該欄位為空:

  • 該值為 null
  • 該值為空字串。
  • 該值為空陣列或空的 Countable 物件。
  • 該值為已上傳的檔案,並且路徑為空。

若有需要使用複雜的邏輯條件來禁止欄位,可使用 Rule::prohibitedIf 方法。該方法接受一個布林值或閉包。傳入閉包時,該閉包應回傳 truefalse,來判斷該驗證欄位是否要被禁止:

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($request->all(), [
5 'role_id' => Rule::prohibitedIf($request->user()->is_admin),
6]);
7 
8Validator::make($request->all(), [
9 'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin),
10]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($request->all(), [
5 'role_id' => Rule::prohibitedIf($request->user()->is_admin),
6]);
7 
8Validator::make($request->all(), [
9 'role_id' => Rule::prohibitedIf(fn () => $request->user()->is_admin),
10]);

prohibited_unless:另一欄位,,...

除非 另一欄位 相符與任意的 ,否則要驗證的欄位必須不存在或為空。當欄位滿足下列條件時,將視該欄位為空:

  • 該值為 null
  • 該值為空字串。
  • 該值為空陣列或空的 Countable 物件。
  • 該值為已上傳的檔案,並且路徑為空。

prohibits:另一欄位,...

若該驗證欄位不存在或為空,則所有 另一欄位 的欄位都必須不存在或為空。當欄位滿足下列條件時,將視該欄位為「空」:

  • 該值為 null
  • 該值為空字串。
  • 該值為空陣列或空的 Countable 物件。
  • 該值為已上傳的檔案,並且路徑為空。

regex:格式

該驗證欄位必須符合給定的正規表示式 (Regular Expression)。

在這個規則內部,使用了 PHP 的 preg_match 函式。指定的格式必須符合 preg_match 所要求的格式,因此必須包含分隔字元(Delimiter)。如:'email' => 'regex:/^.+@.+$/i'

exclamation

使用 regex / not_regex 格式時,可能有需要使用陣列方式制定規則,而不是使用 | 分隔字元。特別是當正規式中有包含 | 字元時。

required

該驗證欄位必須存在於數字資料中且不為空。當欄位滿足下列條件時,將視為「空」:

  • 該值為 null
  • 該值為空字串。
  • 該值為空陣列或空的 Countable 物件。
  • 該值為一無路徑的已上傳檔案。

required_if:另一欄位,,...

另一欄位 符合其中一個 時,該驗證欄位必須存在且不可為空。

若想為 required_if 規則建立更複雜的條件,可使用 Rule::requiredIf 方法。該方法接受一個布林或閉包。傳入閉包時,該閉包應回傳 truefalse 欄判斷該驗證欄位是否為必填 (Required):

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($request->all(), [
5 'role_id' => Rule::requiredIf($request->user()->is_admin),
6]);
7 
8Validator::make($request->all(), [
9 'role_id' => Rule::requiredIf(fn () => $request->user()->is_admin),
10]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rule;
3 
4Validator::make($request->all(), [
5 'role_id' => Rule::requiredIf($request->user()->is_admin),
6]);
7 
8Validator::make($request->all(), [
9 'role_id' => Rule::requiredIf(fn () => $request->user()->is_admin),
10]);

required_unless:另一欄位,,...

除非 另一欄位 符合其中一個 ,否則該驗證欄位必須存在且不可為空。這也表示,除非 null,否則 另一欄位 必須存在於 Request 資料中。若 null (required_unless:name,null),則除非比較的欄位為 null 或比較的欄位不存在於 Request 資料中,否則該驗證欄位為必填(Required)

required_with:foo,bar,...

只有在 任意指定的其他欄位存在且不為空時,該驗證欄位必須存在且不為空。

required_with_all:foo,bar,...

只有在 所有指定的其他欄位都存在且都不為空時,該驗證欄位必須存在且不為空。

required_without:foo,bar,...

只有在 任意指定的其他欄位為空或不存在時,該驗證欄位必須存在且不為空。

required_without_all:foo,bar,...

只有在 所有指定的其他欄位都為空或不存在時,該驗證欄位必須存在且不為空。

required_array_keys:foo,bar,...

The field under validation must be an array and must contain at least the specified keys.

same:欄位

給定的 欄位 必須符合該驗證欄位。

size:

該驗證值必須符合給定 的大小。若為字串資料,則 代表字元數。若為數字(Numeric)資料, 則對應給定的整數值 (該屬性必須同時使用 numericinteger 規則)。若為陣列, 對應到陣列的 count 結果。若為檔案,則 size 對應到單位為 KB(Kilobytes) 的檔案大小。來看看下列範例:

1// 驗證字串為恰好 12 字元長...
2'title' => 'size:12';
3 
4// 驗證提供的整數等於 10...
5'seats' => 'integer|size:10';
6 
7// 驗證陣列恰好有 5 個元素...
8'tags' => 'array|size:5';
9 
10// 驗證上傳檔案的大小為 512 KB...
11'image' => 'file|size:512';
1// 驗證字串為恰好 12 字元長...
2'title' => 'size:12';
3 
4// 驗證提供的整數等於 10...
5'seats' => 'integer|size:10';
6 
7// 驗證陣列恰好有 5 個元素...
8'tags' => 'array|size:5';
9 
10// 驗證上傳檔案的大小為 512 KB...
11'image' => 'file|size:512';

starts_with:foo,bar,...

該驗證欄位必須以其中一個給定的值開頭。

string

該驗證欄位必須為一字串。若想允許該欄位為 null,請為該欄位指定 nullable 規則。

timezone

該欄位必須為 timezone_identifiers_list PHP 函式中的有效時區識別子(Timezone Identifier)

unique:資料表,欄位

該驗證欄位必須不存在於給定資料庫資料表中。

指定自訂的資料表 / 欄位名稱:

除了直接指定資料表名稱外,也可以指定要用來判斷資料表名稱的 Eloquent Model:

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

可使用 欄位 選項來指定該欄位對應的資料庫欄位。若未指定 欄位 選項,則會使用該驗證欄位的名稱。

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

指定自訂資料庫連線

有時候,我們可能需要讓 Validator 在做資料庫查詢時使用自訂的資料庫連線。為此,只需再資料表名稱前方加上連線名稱即可:

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

強制 Unique 規則忽略給定的 ID:

有時候我們可能會想在做 Unique 驗證時忽略給定的 ID。舉例來說,假設我們在「更新個人檔案」頁面,其中包含使用者名稱、電子郵件、位置。我們可能會想驗證這個 E-Mail 是否不重複。不過,若使用者只更改姓名欄位而未更改 E-Mail 欄位,這時因為該使用者已經是這個 E-Mail 位址的擁有者了,所以我們就不會想讓再讓 Validator 跑出驗證錯誤。

若想讓 Validator 忽略該使用者的 ID,我們會需要使用 Rule 類別來流暢地定義該規則。在這個例子中,我們還會使用陣列來定義驗證規則,而不是使用 | 字元來區分各個規則:

1use Illuminate\Database\Eloquent\Builder;
2use Illuminate\Support\Facades\Validator;
3use Illuminate\Validation\Rule;
4 
5Validator::make($data, [
6 'email' => [
7 'required',
8 Rule::unique('users')->ignore($user->id),
9 ],
10]);
1use Illuminate\Database\Eloquent\Builder;
2use Illuminate\Support\Facades\Validator;
3use Illuminate\Validation\Rule;
4 
5Validator::make($data, [
6 'email' => [
7 'required',
8 Rule::unique('users')->ignore($user->id),
9 ],
10]);
exclamation

絕對不要傳入任何由使用者控制的 Request 輸入給 ignore 方法。請只傳入 Eloquent Model 實體中由系統產生的不重複 ID,如自動遞增 ID(Auto-Incrementing ID) 或 UUID。若傳入了使用者控制的資料,可能會讓你的程式發生如 SQL 注入(SQL Injection)等弱點。

除了直接將 Model 的索引鍵值傳給 ignore 方法外,還可以傳入整個 Model 實體。Laravel 會自動從 Model 中取出索引鍵:

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

若你的資料表使用 id 以外的欄位名稱作為主索引鍵,可在呼叫 ignore 方法時指定欄位名稱:

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

預設情況下,unique 規則會檢查欄位名稱符合欲驗證屬性名稱是否不重複。不過,也可以傳入不同的欄位名稱作為第二個引數給 unique 方法:

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

新增額外的 Where 子句:

可以使用 where 方法來自訂查詢,以指定額外的查詢條件。舉例來說,我們來新增一個查詢條件,將該查詢限制在只搜尋 account_id1 的紀錄:

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

uppercase

該驗證欄位必須為大寫字母。

url

該驗證欄位必須為一有效的網址。

ulid

驗證的欄位必須為有效的 ULID (Universally Unique Lexicographically Sortable Identifier)。

uuid

該驗證欄位必須為有效的 RFC 4122 (Version 1, 3, 4, 或 5) 之通用唯一識別碼(Universally Unique Identifier) (UUID)。

有條件地新增規則

當欄位符合特定值時,略過驗證

有時候我們可能會想只在某個欄位為特定值時,才驗證另一個欄位。為此,可以使用 exclude_if 驗證規則。在這個例子中,除非 has_appointment 欄位為 false,否則將不會驗證 appointment_datedoctor_name 欄位:

1use Illuminate\Support\Facades\Validator;
2 
3$validator = Validator::make($data, [
4 'has_appointment' => 'required|boolean',
5 'appointment_date' => 'exclude_if:has_appointment,false|required|date',
6 'doctor_name' => 'exclude_if:has_appointment,false|required|string',
7]);
1use Illuminate\Support\Facades\Validator;
2 
3$validator = Validator::make($data, [
4 'has_appointment' => 'required|boolean',
5 'appointment_date' => 'exclude_if:has_appointment,false|required|date',
6 'doctor_name' => 'exclude_if:has_appointment,false|required|string',
7]);

或者,也可以使用 exclude_unless 規則來在另一個欄位不符合給定值時驗證給定欄位:

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

存在時驗證

在某些情況下,我們會需要 只在 某個欄位存在於資料中,才去驗證該欄位。要快速搞定這個狀況,只需要在規則列表中加上 sometimes 即可:

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

在上述例子中,只有在 $data 陣列中有 email 欄位時,才會驗證該欄位。

lightbulb

若想驗證某個欄位必須存在,但可為空,請參考這個關於可選欄位的備註

複雜的條件式驗證

有時候,我們可能會想以更複雜的條件邏輯來新增驗證規則。舉例來說,我們可能會想在另一個欄位大於 100 時,才要求給定欄位為必填。或者,我們可能需要在某個欄位存在時才驗證某兩個欄位是否有給定的值。要新增這類規則不會很難。首先,先使用不會變動的 靜態規則 來建立 Validator 實體:

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

先假設我們在做一個給遊戲收藏家用的網站。假設某個遊戲收藏家註冊了這個網站,且該收藏家擁有超過 100 款遊戲,我們就想問問這個收藏家位什麼擁有這麼多遊戲。舉例來說,這個收藏家可能在經營二手遊戲店、或者這個收藏家只是很喜歡收藏遊戲而已。若要有條件地新增這個要求,可以在 Validator 實體上使用 sometimes 方法。

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

傳入 sometimes 方法的引數是我們要條件式驗證的欄位名稱。第二個引數是我們要新增的規則列表。若第三個引數的閉包回傳 true,就會新增這些規則。這麼一來,我們就能建立更複雜的條件式驗證了。我們還能一次位多個欄位新增條件式驗證:

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

傳給閉包的 $input 引數會是 Illuminate\Support\Fluent 的實體。且可用來存取所有正在驗證的輸入與檔案。

複雜的條件式陣列驗證

有時候,我們可能會想依據同一個巢狀陣列中的另一個欄位來驗證某個欄位,但同時我們又不知道這個巢狀陣列的索引鍵。在這種情況下,我們可以在閉包中接收第二個引數,該引數位為目前在驗證的陣列中目前的項目:

1$input = [
2 'channels' => [
3 [
4 'type' => 'email',
5 'address' => '[email protected]',
6 ],
7 [
8 'type' => 'url',
9 'address' => 'https://example.com',
10 ],
11 ],
12];
13 
14$validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) {
15 return $item->type === 'email';
16});
17 
18$validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) {
19 return $item->type !== 'email';
20});
1$input = [
2 'channels' => [
3 [
4 'type' => 'email',
5 'address' => '[email protected]',
6 ],
7 [
8 'type' => 'url',
9 'address' => 'https://example.com',
10 ],
11 ],
12];
13 
14$validator->sometimes('channels.*.address', 'email', function (Fluent $input, Fluent $item) {
15 return $item->type === 'email';
16});
17 
18$validator->sometimes('channels.*.address', 'url', function (Fluent $input, Fluent $item) {
19 return $item->type !== 'email';
20});

與傳給閉包的 $input 類似,當屬性資料是陣列時,$item 參數也會是 Illuminate\Support\Fluent 的實體。若非陣列,則會是字串。

驗證陣列

array 驗證規則說明文件中討論過的類似,array 規則接收一個允許的陣列索引鍵列表。若該陣列中有出現其他的索引鍵,會驗證失敗:

1use Illuminate\Support\Facades\Validator;
2 
3$input = [
4 'user' => [
5 'name' => 'Taylor Otwell',
6 'username' => 'taylorotwell',
7 'admin' => true,
8 ],
9];
10 
11Validator::make($input, [
12 'user' => 'array:username,locale',
13]);
1use Illuminate\Support\Facades\Validator;
2 
3$input = [
4 'user' => [
5 'name' => 'Taylor Otwell',
6 'username' => 'taylorotwell',
7 'admin' => true,
8 ],
9];
10 
11Validator::make($input, [
12 'user' => 'array:username,locale',
13]);

一般來說,請總是指定陣列中可出現的索引鍵。如未指定可出現的索引鍵,即使這些索引鍵未經過其他巢狀陣列驗證規則驗證,Validator 的 validate 方法與 validated 方法回傳的所有已驗證中資料,還是會包含該陣列與其所有的索引鍵。

驗證巢狀的陣列輸入

依據表單輸入欄位來驗證巢狀的陣列並不會很難。我們可以使用「『點』標記法(Dot Natation)」來在陣列中驗證屬性。舉例來說,若連入的 HTTP Request 包含了 photos[profile] 欄位,我們可以像這樣驗證該欄位:

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

也可以驗證陣列中的各個元素。舉例來說,若要驗證給定陣列輸入欄位中的各個 E-Mail 是否不重複,可以這麼做:

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

類似的,在語系檔中自訂驗證訊息時,也可以使用 * 字元,讓我們只需要單一驗證訊息就能輕鬆地在陣列欄位上使用:

1'custom' => [
2 'person.*.email' => [
3 'unique' => 'Each person must have a unique email address',
4 ]
5],
1'custom' => [
2 'person.*.email' => [
3 'unique' => 'Each person must have a unique email address',
4 ]
5],

存取巢狀陣列資料

有時候,在為屬性指派認證規則時,我們可能會想存取給定巢狀陣列項目的值。為此,我們可以使用 Rule::forEach 方法來達成。forEach 方法接受一個閉包。在認證時,每次迭代陣列屬性都會叫用一次這個閉包,且該閉包會收到屬性值與完整展開的屬性名稱。該閉包應回傳一個陣列,其中包含要指派給陣列元素的認證規則:

1use App\Rules\HasPermission;
2use Illuminate\Support\Facades\Validator;
3use Illuminate\Validation\Rule;
4 
5$validator = Validator::make($request->all(), [
6 'companies.*.id' => Rule::forEach(function (string|null $value, string $attribute) {
7 return [
8 Rule::exists(Company::class, 'id'),
9 new HasPermission('manage-company', $value),
10 ];
11 }),
12]);
1use App\Rules\HasPermission;
2use Illuminate\Support\Facades\Validator;
3use Illuminate\Validation\Rule;
4 
5$validator = Validator::make($request->all(), [
6 'companies.*.id' => Rule::forEach(function (string|null $value, string $attribute) {
7 return [
8 Rule::exists(Company::class, 'id'),
9 new HasPermission('manage-company', $value),
10 ];
11 }),
12]);

錯誤訊息的索引與位置

在驗證陣列時,有時候我們可能會想在顯示錯誤訊息時參照特定項目的索引或位置。若要參照驗證失敗項目的索引或位置,可在自定驗證訊息中使用 :index (從 0 開始) 與 :position (從 1 開始) 預留位置:

1use Illuminate\Support\Facades\Validator;
2 
3$input = [
4 'photos' => [
5 [
6 'name' => 'BeachVacation.jpg',
7 'description' => 'A photo of my beach vacation!',
8 ],
9 [
10 'name' => 'GrandCanyon.jpg',
11 'description' => '',
12 ],
13 ],
14];
15 
16Validator::validate($input, [
17 'photos.*.description' => 'required',
18], [
19 'photos.*.description.required' => 'Please describe photo #:position.',
20]);
1use Illuminate\Support\Facades\Validator;
2 
3$input = [
4 'photos' => [
5 [
6 'name' => 'BeachVacation.jpg',
7 'description' => 'A photo of my beach vacation!',
8 ],
9 [
10 'name' => 'GrandCanyon.jpg',
11 'description' => '',
12 ],
13 ],
14];
15 
16Validator::validate($input, [
17 'photos.*.description' => 'required',
18], [
19 'photos.*.description.required' => 'Please describe photo #:position.',
20]);

在上述的範例中,會驗證失敗,而使用者會看到這個錯誤訊息:「Please describe photo #2.

驗證檔案

Laravel 提供了多種驗證規則,可用來驗證已上傳的檔案,如 mimesimageminmax。雖然我們也可以自行個別指定這些規則,但 Laravel 還提供了一種能流暢建立檔案驗證規則的建構程式:

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rules\File;
3 
4Validator::validate($input, [
5 'attachment' => [
6 'required',
7 File::types(['mp3', 'wav'])
8 ->min(1024)
9 ->max(12 * 1024),
10 ],
11]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rules\File;
3 
4Validator::validate($input, [
5 'attachment' => [
6 'required',
7 File::types(['mp3', 'wav'])
8 ->min(1024)
9 ->max(12 * 1024),
10 ],
11]);

若專案接受使用者上傳圖片,則可使用 File 規則的 image Constructor 方法來指定這個上傳的檔案應為圖片。此外,使用 dimensions 規則可用來限制圖片的長寬:

1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rules\File;
3 
4Validator::validate($input, [
5 'photo' => [
6 'required',
7 File::image()
8 ->min(1024)
9 ->max(12 * 1024)
10 ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)),
11 ],
12]);
1use Illuminate\Support\Facades\Validator;
2use Illuminate\Validation\Rules\File;
3 
4Validator::validate($input, [
5 'photo' => [
6 'required',
7 File::image()
8 ->min(1024)
9 ->max(12 * 1024)
10 ->dimensions(Rule::dimensions()->maxWidth(1000)->maxHeight(500)),
11 ],
12]);
lightbulb

更多有關驗證圖片長寬的資訊,請參考 dimension 規則的說明文件

檔案類型

雖然在叫用 types 方法時只需要指定副檔名,但該方法其實會實際讀取檔案的內容名推測其 MIME 型別,然後再驗證該檔案實際的 MIME 型別。完整的 MIME 型別列表,以及這些 MIME 對應的副檔名可在下列位置中找到:

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

驗證密碼

若要確定輸入的密碼有足夠的複雜度,可使用 Laravel 的 Password 規則物件:

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

Password 規則物件能讓我們輕鬆地為我們的專案自訂密碼複雜度的要求。例如:我們可以指定密碼必須至少要有一個字母、一個數字、一個符號、或是有大小寫混合的字元:

1// 至少要有 8 字元...
2Password::min(8)
3 
4// 至少要有 1 個英文字母...
5Password::min(8)->letters()
6 
7// 至少要有一個大寫與一個小寫字母...
8Password::min(8)->mixedCase()
9 
10// 至少要有 1 個數字...
11Password::min(8)->numbers()
12 
13// 至少要有 1 個符號...
14Password::min(8)->symbols()
1// 至少要有 8 字元...
2Password::min(8)
3 
4// 至少要有 1 個英文字母...
5Password::min(8)->letters()
6 
7// 至少要有一個大寫與一個小寫字母...
8Password::min(8)->mixedCase()
9 
10// 至少要有 1 個數字...
11Password::min(8)->numbers()
12 
13// 至少要有 1 個符號...
14Password::min(8)->symbols()

此外,還可以使用 uncompromised 方法來確保該密碼在公開的密碼資料外洩(Data Breach)中未曾被入侵:

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

在這個方法內部,Password 規則物件會使用 k-Anonymity 模型來在 haveibeenpwned.com 上以不犧牲使用者隱私或安全性的前提判斷密碼是否有被外洩。

預設情況下,若密碼出現在只少一個資料外洩中,就會被當作已被入侵(Compromised)。我們可以使用 uncompromised 方法的第一個引述來修改這個門檻:

1// 確認密碼在同一個資料外洩中只出現少於 3 次...
2Password::min(8)->uncompromised(3);
1// 確認密碼在同一個資料外洩中只出現少於 3 次...
2Password::min(8)->uncompromised(3);

當然,我們還可以將上述的例子中所有的方法都串在一起:

1Password::min(8)
2 ->letters()
3 ->mixedCase()
4 ->numbers()
5 ->symbols()
6 ->uncompromised()
1Password::min(8)
2 ->letters()
3 ->mixedCase()
4 ->numbers()
5 ->symbols()
6 ->uncompromised()

定義預設的密碼規則

對一些專案來說,在程式中的單一位置內指定預設的密碼驗證規則可能會比較方便。只要使用 Password::defaults 方法就可以輕鬆達成。該方法接受一個閉包,該閉包應回傳預設的 Password 規則設定。一般來說,應在專案內其中一個 Service Provider 中 boot 方法內呼叫這個 defaults 方法:

1use Illuminate\Validation\Rules\Password;
2 
3/**
4 * Bootstrap any application services.
5 */
6public function boot(): void
7{
8 Password::defaults(function () {
9 $rule = Password::min(8);
10 
11 return $this->app->isProduction()
12 ? $rule->mixedCase()->uncompromised()
13 : $rule;
14 });
15}
1use Illuminate\Validation\Rules\Password;
2 
3/**
4 * Bootstrap any application services.
5 */
6public function boot(): void
7{
8 Password::defaults(function () {
9 $rule = Password::min(8);
10 
11 return $this->app->isProduction()
12 ? $rule->mixedCase()->uncompromised()
13 : $rule;
14 });
15}

接著,若要在某個密碼驗證中套用預設規則,只需要呼叫 defaults 方法即可。不需帶任何參數:

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

有時候,除了預設的密碼驗證規則外,我們可能會想附加一些額外的規則上去。為此,可以使用 rules 方法:

1use App\Rules\ZxcvbnRule;
2 
3Password::defaults(function () {
4 $rule = Password::min(8)->rules([new ZxcvbnRule]);
5 
6 // ...
7});
1use App\Rules\ZxcvbnRule;
2 
3Password::defaults(function () {
4 $rule = Password::min(8)->rules([new ZxcvbnRule]);
5 
6 // ...
7});

自訂驗證規則

使用規則物件

Laravel 提供了多種實用的驗證規則。不過,有時候我們可能會想自訂一個規則。要註冊自訂驗證規則的其中一個方法就是使用 Rule 物件。若要產生新的 Rule 物件,可使用 make:rule Artisan 指令。讓我們來使用這個指令產生一個檢查字串是否為大寫的規則。Laravel 會將該規則放在 app/Rules 目錄內。若該目錄不存在,執行這個 Artisan 指令時,Laravel 會自動幫你建立:

1php artisan make:rule Uppercase
1php artisan make:rule Uppercase

建立好規則後,就可以來定義其行為了。Rule 物件只包含了單一方法:validate。該方法會收到屬性的名稱、屬性值、以及一個應在驗證失敗時以錯誤訊息叫用的回呼:

1<?php
2 
3namespace App\Rules;
4 
5use Closure;
6use Illuminate\Contracts\Validation\ValidationRule;
7 
8class Uppercase implements ValidationRule
9{
10 /**
11 * Run the validation rule.
12 */
13 public function validate(string $attribute, mixed $value, Closure $fail): void
14 {
15 if (strtoupper($value) !== $value) {
16 $fail('The :attribute must be uppercase.');
17 }
18 }
19}
1<?php
2 
3namespace App\Rules;
4 
5use Closure;
6use Illuminate\Contracts\Validation\ValidationRule;
7 
8class Uppercase implements ValidationRule
9{
10 /**
11 * Run the validation rule.
12 */
13 public function validate(string $attribute, mixed $value, Closure $fail): void
14 {
15 if (strtoupper($value) !== $value) {
16 $fail('The :attribute must be uppercase.');
17 }
18 }
19}

定義好規則後,就可以與其他驗證規則一起,將 Rule 物件的實體傳給 Validator,以使用該規則:

1use App\Rules\Uppercase;
2 
3$request->validate([
4 'name' => ['required', 'string', new Uppercase],
5]);
1use App\Rules\Uppercase;
2 
3$request->validate([
4 'name' => ['required', 'string', new Uppercase],
5]);

翻譯驗證訊息

除了提供字面錯誤訊息給 $fail 閉包外,也可以提供翻譯字串的索引鍵,並告訴 Laravel 要翻譯這個錯誤訊息:

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

若有需要,translate 方法的第一個引數可以設定預留位置 (Placeholder) 的取代值,第二個引數可以設定偏好的語言:

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

存取額外資料

若這個自訂驗證 Rule 類別需要存取正在驗證的所有其他資料,則可以讓 Rule 類別實作 Illuminate\Contracts\Validation\DataAwareRule 介面。該介面會要求類別要定義 setData 方法。這個方法會由 Laravel (在驗證開始前) 自動叫用,並會傳入所有要驗證的資料:

1<?php
2 
3namespace App\Rules;
4 
5use Illuminate\Contracts\Validation\DataAwareRule;
6use Illuminate\Contracts\Validation\ValidationRule;
7 
8class Uppercase implements DataAwareRule, ValidationRule
9{
10 /**
11 * All of the data under validation.
12 *
13 * @var array<string, mixed>
14 */
15 protected $data = [];
16 
17 // ...
18 
19 /**
20 * Set the data under validation.
21 *
22 * @param array<string, mixed> $data
23 * @return $this
24 */
25 public function setData(array $data): static
26 {
27 $this->data = $data;
28 
29 return $this;
30 }
31}
1<?php
2 
3namespace App\Rules;
4 
5use Illuminate\Contracts\Validation\DataAwareRule;
6use Illuminate\Contracts\Validation\ValidationRule;
7 
8class Uppercase implements DataAwareRule, ValidationRule
9{
10 /**
11 * All of the data under validation.
12 *
13 * @var array<string, mixed>
14 */
15 protected $data = [];
16 
17 // ...
18 
19 /**
20 * Set the data under validation.
21 *
22 * @param array<string, mixed> $data
23 * @return $this
24 */
25 public function setData(array $data): static
26 {
27 $this->data = $data;
28 
29 return $this;
30 }
31}

或者,若這個驗證規則需要存取正在進行驗證的 Validator 實體,則可以實作 ValidatorAwareRule 介面:

1<?php
2 
3namespace App\Rules;
4 
5use Illuminate\Contracts\Validation\ValidationRule;
6use Illuminate\Contracts\Validation\ValidatorAwareRule;
7use Illuminate\Validation\Validator;
8 
9class Uppercase implements ValidationRule, ValidatorAwareRule
10{
11 /**
12 * The validator instance.
13 *
14 * @var \Illuminate\Validation\Validator
15 */
16 protected $validator;
17 
18 // ...
19 
20 /**
21 * Set the current validator.
22 */
23 public function setValidator(Validator $validator): static
24 {
25 $this->validator = $validator;
26 
27 return $this;
28 }
29}
1<?php
2 
3namespace App\Rules;
4 
5use Illuminate\Contracts\Validation\ValidationRule;
6use Illuminate\Contracts\Validation\ValidatorAwareRule;
7use Illuminate\Validation\Validator;
8 
9class Uppercase implements ValidationRule, ValidatorAwareRule
10{
11 /**
12 * The validator instance.
13 *
14 * @var \Illuminate\Validation\Validator
15 */
16 protected $validator;
17 
18 // ...
19 
20 /**
21 * Set the current validator.
22 */
23 public function setValidator(Validator $validator): static
24 {
25 $this->validator = $validator;
26 
27 return $this;
28 }
29}

使用閉包

若在專案中只有一個地方會需要某個自訂驗證規則,除了使用 Rule 物件外,我們可以使用閉包。這個閉包會收到屬性名稱、屬性值、以及一個要在驗證失敗時呼叫的 $fail 回呼:

1use Illuminate\Support\Facades\Validator;
2 
3$validator = Validator::make($request->all(), [
4 'title' => [
5 'required',
6 'max:255',
7 function (string $attribute, mixed $value, Closure $fail) {
8 if ($value === 'foo') {
9 $fail("The {$attribute} is invalid.");
10 }
11 },
12 ],
13]);
1use Illuminate\Support\Facades\Validator;
2 
3$validator = Validator::make($request->all(), [
4 'title' => [
5 'required',
6 'max:255',
7 function (string $attribute, mixed $value, Closure $fail) {
8 if ($value === 'foo') {
9 $fail("The {$attribute} is invalid.");
10 }
11 },
12 ],
13]);

隱式規則

預設情況下,若正在驗證的屬性不存在或包含空字串時,就不會執行包含自訂規則在內的一般驗證規則。舉例來說,遇到空字串時 unique 規則將不會執行:

1use Illuminate\Support\Facades\Validator;
2 
3$rules = ['name' => 'unique:users,name'];
4 
5$input = ['name' => ''];
6 
7Validator::make($input, $rules)->passes(); // true
1use Illuminate\Support\Facades\Validator;
2 
3$rules = ['name' => 'unique:users,name'];
4 
5$input = ['name' => ''];
6 
7Validator::make($input, $rules)->passes(); // true

如果要在屬性為空時也執行自定規則,則該規則必須暗示該屬性為 required。若要產生新的隱式規則物件,可在呼叫 make:rule Artisan 指令時提供 --implicit 選項:

1php artisan make:rule Uppercase --implicit
1php artisan make:rule Uppercase --implicit
exclamation

「隱式」規則只 暗示 該屬性為必填欄位。至於當屬性不存在或屬性為空時是否要視為驗證失敗,則取決於你。

翻譯進度
94% 已翻譯
更新時間:
2024年6月30日 上午8:27:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.