[ Laravel 5.8 文件 ] 基礎元件 —— 表單驗證
簡介
Laravel 提供了多種方法來驗證請求輸入資料。預設情況下,Laravel 的控制器基類使用 ValidatesRequests
trait,該 trait 提供了便捷方法通過各種功能強大的驗證規則來驗證輸入的 HTTP 請求。
快速入門
要掌握 Laravel 強大的驗證特性,讓我們先看一個完整的驗證表單並返回錯誤資訊給使用者的示例。
定義路由
首先,我們假定在 routes/web.php
檔案中包含如下路由:
// 顯示建立部落格文章表單... Route::get('post/create', 'PostController@create'); // 儲存新的部落格文章... Route::post('post', 'PostController@store');
顯然, GET
路由為使用者顯示了一個建立新的部落格文章的表單, POST
路由將新的部落格文章儲存到資料庫。
建立控制器
接下來,讓我們看一個處理這些路由的簡單控制器示例。我們先將 store
方法留空:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PostController extends Controller { /** * 顯示建立新的部落格文章的表單 * * @return Response */ public function create() { return view('post.create'); } /** * 儲存新的部落格文章 * * @paramRequest$request * @return Response */ public function store(Request $request) { // 驗證並存儲部落格文章... } }
編寫驗證邏輯
現在我們準備用驗證新部落格文章輸入的邏輯填充 store
方法。我們使用 Illuminate\Http\Request
物件提供的 validate
方法來實現這一功能,如果驗證規則通過,程式碼將會繼續往下執行;反之,如果驗證失敗,將會丟擲一個異常,相應的錯誤響應也會自動傳送給使用者。在這個傳統的 HTTP 請求案例中,將會生成一個重定向響應,如果是 AJAX 請求則會返回一個 JSON 響應。
要更好地理解 validate
方法,讓我們回顧下 store
方法:
/** * 儲存部落格文章 * * @paramRequest$request * @return Response */ public function store(Request $request){ $validatedData = $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); // 驗證通過,儲存到資料庫... }
正如你所看到的,我們只是傳入期望的驗證規則到 validate
方法。再強調一次,如果驗證失敗,相應的響應會自動生成。如果驗證通過,控制器將會繼續往下執行。
注:實際執行程式碼之前,需要在資料庫中建立 posts
資料表,因為這裡用到了 unique:posts
這個驗證規則,該規則會去資料庫中查詢傳入標題是否已存在以保證唯一性。
首次驗證失敗後中止後續規則驗證
有時候你可能想要在首次驗證失敗後停止檢查該屬性的其它驗證規則,要實現這個功能,可以在規則屬性中分配 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.desc' => 'required', ]);
這樣的驗證規則適用於驗證如下標籤請求:
<form method="POST" action="{{route('posts.store')}}"> {{csrf_field()}} <input type="text" name="title"/> <input type="text" name="author[name]"/> <input type="text" name="author[desc]"/> <textarea cols="20" rows="5" name="body"></textarea> <button type="submit">submit</button> </form>
顯示驗證錯誤資訊
那麼,如果請求輸入引數沒有通過給定驗證規則怎麼辦?正如前面所提到的,Laravel 將會自動將使用者重定向回上一個位置。此外,所有驗證錯誤資訊會自動存放到一次性 Session,我們可以看下這個 Session 資料的資料結構:
注意我們並沒有在 GET
路由中顯式繫結錯誤資訊到檢視。這是因為 Laravel 總是從 Session 資料中檢查錯誤資訊,而且如果有的話會自動將其繫結到檢視。所以,值得注意的是每次請求的所有檢視中總是存在一個 $errors
變數,從而允許你在檢視中方便而又安全地使用。 $errors
變數是一個 Illuminate\Support\MessageBag
例項。想要了解更多關於該物件的資訊,檢視其對應文件。
注: $errors
變數會通過 web
中介軟體組中的 Illuminate\View\Middleware\ShareErrorsFromSession
中介軟體繫結到檢視,如果使用了該中介軟體,那麼 $errors
變數在檢視中總是有效,從而方便你隨時使用。
所以,在我們的例子中,驗證失敗的話使用者將會被重定向到控制器的 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自帶了 TrimStrings
和 ConvertEmptyStringsToNull
中介軟體,這兩個中介軟體位於 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
判定為無效日期。
AJAX 請求 & 驗證
在上面的例子中,我們使用了傳統的表單來發送資料到應用。不過,現實場景中,很多應用使用 AJAX 請求。在 AJAX 請求中使用 validate
方法時,Laravel 不會生成重定向響應。取而代之的,Laravel 生成一個包含驗證錯誤資訊的 JSON 響應。該 JSON 響應會帶上一個 HTTP 狀態碼 422
。
表單請求驗證
建立表單請求
對於更復雜的驗證場景,你可能想要建立一個“表單請求”。表單請求是包含驗證邏輯的自定義請求類,要建立表單驗證類,可以使用 Artisan 命令 make:request
:
php artisan make:request StoreBlogPost
生成的類位於 app/Http/Requests
目錄下,如果該目錄不存在,執行 make:request
命令時會替我們生成。接下來我們新增少許驗證規則到該類的 rules
方法:
/** * 獲取應用到請求的驗證規則 * * @return array */ public function rules(){ return [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]; }
注:你可以在 rules
方法簽名簽名中注入任何依賴,它們會通過服務容器自動解析。
那麼,驗證規則如何生效呢?你所要做的就是在控制器方法中型別提示該請求類。這樣表單輸入請求會在控制器方法被呼叫之前被驗證,這就是說你不需要將控制器方法和驗證邏輯雜糅在一起:
/** * 儲存輸入的部落格文章 * * @paramStoreBlogPostRequest$request * @return Response */ public function store(StoreBlogPost $request){ // The incoming request is valid... // Retrieve the validated input data... $validated = $request->validated(); }
如果驗證失敗,重定向響應會被生成並將使用者退回上一個位置,錯誤資訊也會被儲存到一次性 Session 以便在檢視中顯示。如果是 AJAX 請求,帶 422
狀態碼的 HTTP 響應將會返回給使用者,該響應資料中還包含了 JSON 格式的驗證錯誤資訊。
新增驗證後鉤子到表單請求
如果你想要新增“驗證後”鉤子到表單請求,可以使用 withValidator
方法。該方法接收完整的構造驗證器,從而允許你在驗證規則執行前呼叫任何驗證器方法:
/** * 配置驗證器例項. * * @param\Illuminate\Validation\Validator$validator * @return void */ public function withValidator($validator) { $validator->after(function ($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } }); }
授權表單請求
表單請求類還包含了一個 authorize
方法,你可以通過該方法檢查認證使用者是否有許可權更新指定資源。例如,如果使用者嘗試更新一條部落格評論,那麼他必須是該評論的所有者。舉個例子:
/** * 判定使用者是否有許可權發起請求. * * @return bool * @translator laravelacademy.org */ public function authorize() { $comment = Comment::find($this->route('comment')); return $comment && $this->user()->can('update', $comment); }
由於所有請求都繼承自 Laravel 請求基類,我們可以使用 user
方法獲取當前認證使用者,還要注意上面這個例子中對 route
方法的呼叫。該方法賦予使用者訪問被呼叫路由 URI 引數的許可權,比如下面這個例子中的 {comment}
引數:
Route::post('comment/{comment}');
如果 authorize
方法返回 false
,一個包含 403
狀態碼的 HTTP 響應會自動返回而且控制器方法將不會被執行。
如果你計劃在應用的其他部分呼叫授權邏輯,只需在 authorize
方法中簡單返回 true
即可:
/** * 判斷請求使用者是否經過授權 * * @return bool */ public function authorize(){ return true; }
注:你可以在 authorize()
方法簽名中注入任何依賴,它們將會通過服務容器自動解析。
自定義錯誤訊息
你可以通過重寫 messages
方法自定義表單請求使用的錯誤訊息,該方法應該返回屬性/規則對陣列及其對應錯誤訊息:
/** * 獲取被定義驗證規則的錯誤訊息 * * @return array * @translator laravelacademy.org */ 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 */ public function attributes() { return [ 'email' => 'email address', ]; }
手動建立驗證器
如果你不想使用請求例項上的 validate
方法,可以使用 Validator
門面手動建立一個驗證器例項,該門面提供的 make
方法可用於生成一個新的驗證器例項:
<?php namespace App\Http\Controllers; use Validator; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class PostController extends Controller{ /** * 儲存新的部落格文章 * * @paramRequest$request * @return Response */ public function store(Request $request) { $validator = Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); if ($validator->fails()) { return redirect('post/create') ->withErrors($validator) ->withInput(); } // 儲存部落格文章... } }
傳遞給 make
方法的第一個引數是需要驗證的資料,第二個引數是要應用到資料上的驗證規則。
檢查請求沒有通過驗證後,可以使用 withErrors
方法將錯誤資料存放到一次性 Session,使用該方法時, $errors
變數重定向後自動在檢視間共享,從而允許你輕鬆將其顯示給使用者, withErrors
方法接收一個驗證器、或者一個 MessageBag
,又或者一個 PHP 陣列。
自動重定向
如果你想要手動建立一個驗證器例項,但仍然使用請求例項的 validate
方法提供的自動重定向,可以呼叫已存在驗證器例項上的 validate
方法,如果驗證失敗,使用者將會被自動重定向,或者,如果是 AJAX 請求的話,返回 JSON 響應:
Validator::make($request->all(), [ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ])->validate();
命名錯誤包
如果你在單個頁面上有多個表單,可能需要命名錯誤的 MessageBag
,從而允許你為指定表單獲取錯誤資訊。只需要傳遞名稱作為第二個引數給 withErrors
即可:
return redirect('register') ->withErrors($validator, 'login');
然後你就可以從 $errors
變數中訪問命名的 MessageBag
例項:
{{ $errors->login->first('email') }}
驗證鉤子之後
驗證器允許你在驗證完成後添加回調,這種機制允許你輕鬆執行更多驗證,甚至新增更多錯誤資訊到訊息集合。使用驗證器例項上的 after
方法即可:
$validator = Validator::make(...); $validator->after(function($validator) { if ($this->somethingElseIsInvalid()) { $validator->errors()->add('field', 'Something is wrong with this field!'); } }); if ($validator->fails()) { // }
處理錯誤資訊
呼叫 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’)) { // }
自定義錯誤資訊
如果需要的話,你可以使用自定義錯誤資訊替代預設的,有多種方法來指定自定義資訊。首先,你可以傳遞自定義資訊作為第三個引數給 Validator::make
方法:
$messages = [ 'required' => 'The :attribute field is required.', ]; $validator = Validator::make($input, $rules, $messages);
在本例中, :attribute
佔位符將會被驗證時實際的欄位名替換,你還可以在驗證訊息中使用其他佔位符,例如:
$messages = [ 'same'=> 'The :attribute and :other must match.', 'size'=> 'The :attribute must be exactly :size.', 'between' => 'The :attribute must be between :min - :max.', 'in'=> 'The :attribute must be one of the following types: :values', ];
為給定屬性指定自定義資訊
有時候你可能只想為特定欄位指定自定義錯誤資訊,可以通過“.”來實現,首先指定屬性名,然後是規則:
$messages = [ 'email.required' => '郵箱地址不能為空!', ];
在語言檔案中指定自定義訊息
在很多案例中,你可能想要在語言檔案中指定自定義訊息而不是將它們直接傳遞給 Validator
。要實現這個,新增訊息到 resources/lang/xx/validation.php
語言檔案的 custom
陣列:
'custom' => [ 'email' => [ 'required' => '郵箱地址不能為空!', ], ],
在語言檔案中指定自定義屬性
如果你想要將驗證訊息的 :attribute
部分替換成自定義的屬性名稱,可以在語言檔案 resources/lang/xx/validation.php
的 attributes
陣列中指定自定義名稱:
'attributes' => [ 'email' => '郵箱地址', ],
在語言檔案中指定自定義值
有時候你可能需要將驗證訊息中的 :value
部分替換成自定義的表示值,例如,下面這個驗證規則指定如果 payment_type
值是 cc
的話信用卡號不能為空:
$request->validate([ 'credit_card_number' => 'required_if:payment_type,cc' ]);
如果這個驗證規則驗證失敗了,將會生成如下錯誤訊息:
The credit card number field is required when payment type is cc.
如果你想要替換支付型別值 cc
,可以在 validation
語言檔案中定義一個 values
陣列來指定自定義的表示值:
'values' => [ 'payment_type' => [ 'cc' => 'credit card' ], ],
這樣一來,如果驗證規則驗證失敗,對應的錯誤資訊如下:
The credit card number field is required when payment type is credit card.
驗證規則大全
下面是有效規則及其函式列表:
- Accepted
- Active URL
- After (Date)
- After Or Equal(Date)
- Alpha
- Alpha Dash
- Alpha Numeric
- Array
- Bail
- Before (Date)
- Before Or Equal(Date)
- Between
- Boolean
- Confirmed
- Date
- Date Equals
- Date Format
- Different
- Digits
- Digits Between
- Dimensions(圖片檔案)
- Distinct
- 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
- Max
- MIME Types (File)
- MIME Type By File Extension
- Min
- Not In
- Not Regex
- Nullable
- Numeric
- Present
- Regular Expression
- Required
- Required If
- Required Unless
- Required With
- Required With All
- Required Without
- Required Without All
- Same
- Size
- Starts With
- String
- Timezone
- Unique (Database)
- URL
- UUID
accepted
驗證欄位的值必須是 yes
、 on
、 1
或 true
,這在「同意服務協議」時很有用。
active_url
驗證欄位必須是基於 PHP 函式 dns_get_record
的,有 A 或 AAAA 記錄的值。
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
驗證欄位必須是字母。
alpha_dash
驗證欄位可以包含字母和數字,以及破折號和下劃線。
alpha_num
驗證欄位必須是字母或數字。
array
驗證欄位必須是 PHP 陣列。
bail
第一個驗證規則驗證失敗則停止執行其它驗證規則。
before:date
和 after:date
相對,驗證欄位必須是指定日期之前的一個數值,日期將會傳遞給 PHP strtotime
函式。
before_or_equal:date
驗證欄位必須小於等於給定日期。日期將會傳遞給 PHP 的 strtotime
函式。
between:min,max
驗證欄位大小在給定的最小值和最大值之間,字串、數字、陣列和檔案都可以像使用 size
規則一樣使用該規則:
'name' => 'required|between:1,20'
boolean
驗證欄位必須可以被轉化為布林值,接收 true
, false
, 1
, 0
, "1"
和 "0"
等輸入。
confirmed
驗證欄位必須有一個匹配欄位 foo_confirmation
,例如,如果驗證欄位是 password
,必須輸入一個與之匹配的 password_confirmation
欄位。
date
驗證欄位必須是一個基於 PHP strtotime
函式的有效日期
date_equals:date
驗證欄位必須等於給定日期,日期會被傳遞到 PHP strtotime
函式。
date_format:format
驗證欄位必須匹配指定格式,可以使用 PHP 函式date 或 date_format 驗證該欄位。
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'
驗證欄位必須是格式正確的電子郵件地址。
exists:table,column
驗證欄位必須存在於指定資料表。
基本使用:
'state' => 'exists:states'
如果 column
選項沒有指定,將會使用欄位名。
指定自定義列名:
'state' => 'exists:states,abbreviation'
有時,你可能需要為 exists
查詢指定要使用的資料庫連線,這可以在表名前通過.前置資料庫連線來實現:
'email' => 'exists:connection.staff,email'
如果你想要自定義驗證規則執行的查詢,可以使用 Rule
類來定義規則。在這個例子中,我們還以陣列形式指定了驗證規則,而不是使用 |
字元來限定它們:
use Illuminate\Validation\Rule; Validator::make($data, [ 'email' => [ 'required', Rule::exists('staff')->where(function ($query) { $query->where('account_id', 1); }), ], ]);
file
驗證欄位必須是上傳成功的檔案。
filled
驗證欄位如果存在則不能為空。
gt:field
驗證欄位必須大於給定 field
欄位,這兩個欄位型別必須一致,適用於字串、數字、陣列和檔案,和 size
規則類似
gte:field
驗證欄位必須大於等於給定 field
欄位,這兩個欄位型別必須一致,適用於字串、數字、陣列和檔案,和 size
規則類似
image
驗證檔案必須是圖片(jpeg、png、bmp、gif 或者 svg)
in:foo,bar…
驗證欄位值必須在給定的列表中,由於該規則經常需要我們對陣列進行 implode
,我們可以使用 Rule::in
來構造這個規則:
use Illuminate\Validation\Rule; Validator::make($data, [ 'zones' => [ 'required', Rule::in(['first-zone', 'second-zone']), ], ]);
in_array:另一個欄位
驗證欄位必須在 另一個欄位 值中存在。
integer
驗證欄位必須是整型。
ip
驗證欄位必須是IP地址。
ipv4
驗證欄位必須是IPv4地址。
ipv6
驗證欄位必須是IPv6地址。
json
驗證欄位必須是有效的JSON字串
lt:field
驗證欄位必須小於給定 field
欄位,這兩個欄位型別必須一致,適用於字串、數字、陣列和檔案,和 size
規則類似
lte:field
驗證欄位必須小於等於給定 field
欄位,這兩個欄位型別必須一致,適用於字串、數字、陣列和檔案,和 size
規則類似
max:value
驗證欄位必須小於等於最大值,和字串、數值、陣列、檔案欄位的 size
規則使用方式一樣。
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
規則使用方式一致。
not_in:foo,bar,…
驗證欄位值不能在給定列表中,和 in
規則類似,我們可以使用 Rule::notIn
方法來構建規則:
use Illuminate\Validation\Rule; Validator::make($data, [ 'toppings' => [ 'required', Rule::notIn(['sprinkles', 'cherries']), ], ]);
not_regex:pattern
驗證欄位不能匹配給定正則表示式
注:使用 regex
/ not_regex
模式時,規則必須放在陣列中,而不能使用管道分隔符,尤其是正則表示式中包含管道符號時。
nullable
驗證欄位可以是 null
,這在驗證一些可以為 null
的原始資料如整型或字串時很有用。
numeric
驗證欄位必須是數值
present
驗證欄位必須出現在輸入資料中但可以為空。
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
方法,該方法接收一個布林值或閉包。當傳遞一個閉包時,會返回 true
或 false
以表明驗證欄位是否是必須的:
use Illuminate\Validation\Rule; Validator::make($request->all(), [ 'role_id' => Rule::requiredIf($request->user()->is_admin), ]); Validator::make($request->all(), [ 'role_id' => Rule::requiredIf(function () use ($request) { return $request->user()->is_admin; }), ]);
required_unless:anotherfield,value,…
除非 anotherfield
欄位等於 value
,否則驗證欄位不能空。
required_with:foo,bar,…
驗證欄位只有在任一其它指定欄位存在的情況才是必須的。
required_with_all:foo,bar,…
驗證欄位只有在所有指定欄位存在的情況下才是必須的。
required_without:foo,bar,…
驗證欄位只有當任一指定欄位不存在的情況下才是必須的。
required_without_all:foo,bar,…
驗證欄位只有當所有指定欄位不存在的情況下才是必須的。
same:field
給定欄位和驗證欄位必須匹配。
size:value
驗證欄位必須有和給定值 value
相匹配的尺寸/大小,對字串而言, value
是相應的字元數目;對數值而言, value
是給定整型值;對陣列而言, value
是陣列長度;對檔案而言, value
是相應的檔案千位元組數(KB)。
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
方法之外,你還可以傳遞整個模型例項。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_id
為 1
的約束:
'email' => Rule::unique('users')->where(function ($query) { $query->where('account_id', 1); })
url
驗證欄位必須是有效的 URL。
uuid
該驗證欄位必須是有效的 RFC 4122(版本 1、3、4 或 5)全域性唯一識別符號(UUID)。
新增條件規則
存在時驗證
在某些場景下,你可能想要只有某個欄位存在的情況下進行驗證檢查,要快速實現這個,新增 sometimes
規則到規則列表:
$v = Validator::make($data, [ 'email' => 'sometimes|required|email', ]);
在上例中, email
欄位只有存在於 $data
陣列時才會被驗證。
注:如果你嘗試驗證一個總是存在但可能為空的欄位時,參考可選欄位注意事項。
複雜條件驗證
有時候你可能想要基於更復雜的條件邏輯新增驗證規則。例如,你可能想要只有在另一個欄位值大於 100 時才要求一個給定欄位是必須的,或者,你可能需要只有當另一個欄位存在時兩個欄位才都有給定值。新增這個驗證規則並不是一件頭疼的事。首先,建立一個永遠不會改變的靜態規則到 Validator
例項:
$v = Validator::make($data, [ 'email' => 'required|email', 'games' => 'required|numeric', ]);
讓我們假定我們的 Web 應用服務於遊戲收藏者。如果一個遊戲收藏者註冊了我們的應用並擁有超過 100 個遊戲,我們想要他們解釋為什麼他們會有這麼多遊戲,例如,也許他們在運營一個遊戲二手店,又或者他們只是喜歡收藏。要新增這種條件,我們可以使用 Validator
例項上的 sometimes
方法:
$v->sometimes('reason', 'required|max:500', function($input) { return $input->games >= 100; });
傳遞給 sometimes
方法的第一個引數是我們需要有條件驗證的名稱欄位,第二個引數是我們想要新增的規則,如果作為第三個引數的閉包返回 true
,規則被新增。該方法讓構建複雜條件驗證變得簡單,你甚至可以一次為多個欄位新增條件驗證:
$v->sometimes(['reason', 'cost'], 'required', function($input) { return $input->games >= 100; });
注:傳遞給閉包的 $input
引數是 Illuminate\Support\Fluent
的一個例項,可用於訪問輸入和檔案。
驗證陣列輸入
驗證表單陣列輸入欄位不再是件痛苦的事情,例如,如果進入的 HTTP 請求包含 photos[profile]
欄位,可以這麼驗證:
$validator = Validator::make($request->all(), [ 'photos.profile' => 'required|image', ]);
我們還可以驗證陣列的每個元素,例如,要驗證給定陣列輸入中每個 email
是否是唯一的,可以這麼做(這種針對提交的陣列欄位是二維陣列,如 person[][email]
或 person[test][email]
):
$validator = Validator::make($request->all(), [ 'person.*.email' => 'email|unique:users', 'person.*.first_name' => 'required_with:person.*.last_name', ]);
類似地,在語言檔案中你也可以使用 *
字元指定驗證訊息,從而可以使用單個驗證訊息定義基於陣列欄位的驗證規則:
'custom' => [ 'person.*.email' => [ 'unique' => '每個人的郵箱地址必須是唯一的', ] ],
自定義驗證規則
使用 Rule 物件
如上所述,Laravel 提供了多種有用的驗證規則;不過,你可能還是需要指定一些自己的驗證規則。註冊自定義驗證規則的一種方法是使用規則物件,要生成一個新的規則物件,可以使用 Artisan 命令 make:rule
。下面我們使用這個命令來生成一個用於驗證字串是否是大寫的規則,生成的新規則物件類位於 app/Rules
目錄:
php artisan make:rule Uppercase
規則建立之後,就可以定義行為方法,一個規則物件包含兩個方法: passes
和 message
, passes
方法接收屬性值和名稱,並且基於屬性值是否有效返回 true
或 false
。 message
方法用於在驗證失敗時返回驗證錯誤訊息:
<?php namespace App\Rules; use Illuminate\Contracts\Validation\Rule; class Uppercase implements Rule { /** * Determine if the validation rule passes. * * @paramstring$attribute * @parammixed$value * @return bool */ public function passes($attribute, $value) { return strtoupper($value) === $value; } /** * Get the validation error message. * * @return string */ public function message() { return 'The :attribute must be uppercase.'; } }
當然,你可以在 message
方法中呼叫輔助函式 trans
來返回一個在語言檔案中定義的錯誤訊息:
/** * Get the validation error message. * * @return string */ public function message() { return trans('validation.uppercase'); }
規則定義好之後,就可以將其以規則物件例項的方式和其他驗證規則一起提供給驗證器:
use App\Rules\Uppercase; $request->validate([ 'name' => ['required', new Uppercase], ]);
使用閉包
如果在整個應用只需要一次自定義規則的功能,可以使用閉包替代規則物件。該閉包接收屬性名、屬性值以及驗證失敗時呼叫的 $fail
回撥:
$validator = Validator::make($request->all(), [ 'title' => [ 'required', 'max:255', function($attribute, $value, $fail) { if ($value === 'foo') { return $fail($attribute.' is invalid.'); } }, ], ]);
使用擴充套件
另一個註冊自定義驗證規則的方式是使用 Validator
門面上的 extend
方法。我們在某個服務提供者(如 AppServiceProvider
)中使用該方法註冊一個自定義驗證規則:
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Validator; class AppServiceProvider extends ServiceProvider { /** * 啟動應用服務 * * @return void */ public function boot() { Validator::extend('foo', function($attribute, $value, $parameters, $validator) { return $value == 'foo'; }); } /** * 註冊服務提供者 * * @return void */ public function register() { // } }
自定義驗證器閉包接收四個引數:要驗證的屬性名稱、屬性值、傳遞給規則的引數陣列以及 Validator
例項。
你還可以傳遞類和方法到 extend
方法而不是閉包:
Validator::extend('foo', 'FooValidator@validate');
定義錯誤資訊
你還需要為自定義規則定義錯誤資訊。你可以使用內聯自定義訊息陣列或者在驗證語言檔案中新增條目來實現這一功能。訊息應該被放到陣列的第一維,而不是在只用於存放屬性指定錯誤資訊的 custom
陣列內:
"foo" => "Your input was invalid!", "accepted" => "The :attribute must be accepted.", // 驗證錯誤資訊其它部分...
當建立一個自定義驗證規則時,你可能有時候需要為錯誤資訊定義自定義佔位符,可以通過建立自定義驗證器然後呼叫 Validator
門面上的 replacer
方法來實現。在服務提供者的 boot
方法中編寫如下程式碼:
/** * 啟動應用服務 * * @return void * @translator laravelacademy.org */ public function boot(){ Validator::extend(...); Validator::replacer('foo', function($message, $attribute, $rule, $parameters) { return str_replace(...); }); }
隱式擴充套件
預設情況下,被驗證的屬性如果沒有提供或者驗證規則為 required
而值為空,那麼正常的驗證規則,包括自定義擴充套件將不會執行。例如, unique
規則將不會檢驗 null
值:
$rules = ['name' => 'unique']; $input = ['name' => null]; Validator::make($input, $rules)->passes(); // true
如果要求即使為空時也要驗證屬性,則必須要暗示屬性是必須的,要建立一個隱式擴充套件,可以使用 Validator::extendImplicit()
方法:
Validator::extendImplicit('foo', function($attribute, $value, $parameters, $validator) { return $value == 'foo'; });
注:一個隱式擴充套件僅僅暗示屬性是必須的,至於它到底是缺失的還是空值這取決於你。