Server 對於收到的資料都應該要進行驗證確保 server 的安全以及過濾不必要的錯誤。然而自己寫資料驗證頗為麻煩,從昨天的 controller 範例,也看得出來,光是驗證「使用者 ID」和 「文章 ID」,就寫了一大串的判斷。今天依照下面主題逐步介紹 Laravel 提供的驗證機制,讓驗證易寫、好讀、方便維護。
validate
方法。validate
所帶的參數為 array,其中
以下面的程式為例,我們要驗證 request 帶進來的 userId
、postId
、title
和 content
四個欄位。
function edit(Request $request)
{
$validResult = $request->validate([
"userId" => "required|integer|exists:users,id",
"postId" => "required|integer|exists:posts,id",
"title" => "nullable|string",
"content" => "nullable|string"
]);
}
ValidationException
提取更多細節: function edit(Request $request)
{
try {
$validResult = $request->validate([
"userId" => "required|integer|exists:users,id",
"postId" => "required|integer|exists:posts,id",
"title" => "nullable|string",
"content" => "nullable|string"
]);
} catch (ValidationException $exception) {
// 取得 laravel Validator 實例
$validatorInstance = $exception->validator;
// 取得錯誤資料
$errorMessageData = $validationInstance->getMessageBag();
// 取得驗證錯誤訊息
$errorMessages = $errorMessageData->getMessages();
}
}
最終驗證結果 $errorMessages
會是一個 array,分別記錄各別欄位錯誤 (如下圖)。
Laravel 驗證方法是由「方法名稱」和「條件值」兩個部分組成並用冒號隔開如: <name>:<condition_values>
。以下列出部份個人覺得常用的驗證方法,完整列表可參考官網。
required
: 該欄位的值必須符合下列條件
nullable
: 欄位值可為 null,且值為 null 時,其他驗證方法不會因為 null 而報錯present
: request 必須要有該欄位,但可以是空值sometimes
: 當 request 有該欄位才會進行驗證true
、false
、1
、0
、"1"
和 "0"
string
: 欄位值必須為字串start_with:foo,bar,...
: 該欄位值必須是以條件之一做為開頭end_with:foo,bar,...
: 欄位值必須是以條件之一做為結束regex:pattern
: 欄位值必須符合自訂的正則表示式,如: 'phone' => 'regex:/^09\d{8}$/i'
integer
: 欄位值必須為整數numeric
: 欄位值必須為數值file
: 欄位值必須是一個成功上傳的檔案mimetypes:text/plain,...
: 上傳的檔案類型,必須符合條件之一的 MIME 類型,如: 'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime'
mimes:foo,bar,...
: 上傳的檔案類型,必須符合條件之一所對應的 MIME 類型,如: 'photo' => 'mimes:jpeg,bmp,png'
size:value
: 欄位值必須符合條件設定的「整數大小」,各項資料大小定義如下:
max:value
: 欄位值最大「不能超過」條件大小min:value
: 欄位值最小「不能不足」條件大小exists:table,column
: 在條件設定的「資料表欄位」中,必須存在欄位值,如 'userId' => 'exits:users,id'
unique:table,column,except,idColumn
: 在條件設定的「資料表欄位」中,不可存有欄位值,如 'email' => 'exits:users,email'
bail
: 欄位驗證過程中,只要有一個驗證方法不通過便停止驗證confirmed
: request 中必須要有一個「<該欄位名稱>_confirmation」的欄位,同時兩者的值要完全相同,如: 'password' => 'confirmed'
,則 request 中必須有 password_confirmation
,且兩個欄位的值必須相同email
: 欄位值必須符合 email 的命名規則不論是語系的關係或是應用上的需求,滿常需要客製化錯誤訊息,客製方法如下。其中,客製訊息的 key 是由 「欄位名稱.驗證方法名稱」 格式命名 (輸出錯誤結果如圖)。
function edit(Request $request)
{
try {
$rules = [
"userId" => "required|integer|exists:users,id",
"postId" => "required|integer|exists:posts,id",
];
$message = [
// 欄位名稱.驗證方法名稱
"userId.required" => "使用者 ID 為避填資料",
"userId.exists" => '使用者 ID 必須存在於資料庫中',
"postId.integer" => "文章 ID 必須為數值",
"postId.exists" => "文章 ID 不存在"
];
$validResult = $request->validate($rules, $message);
} catch (ValidationException $exception) {
$errorMessage =
$exception->validator->getMessageBag()->getMessages();
}
}
如果要修改系統所有預設訊息,可以到 resources/lang/en/validation.php
修改,或是建立需要語系的資料夾以及檔案,如 resources/lang/zh_tw/validation.php
。
Laravel 也支援巢狀資料的驗證,跟基本驗證相同,唯一差異就是欄位名稱的部分必須包含所有上層的欄位名稱。例如 request body 如下:
{
"user": {
"id": 10,
},
"post": {
"id": 5,
"title": "Day 09. Request 驗證可以再簡單一點 (Validation)",
"content": null,
}
}
則驗證規則寫法為:
function edit(Request $request)
{
try {
$validResult = $request->validate([
// 可以解釋為: user 的 id
"user.id" => "required|integer|exists:users,id",
"post.id" => "required|integer|exists:posts,id",
"post.title" => "nullable|string",
"post.content" => "nullable|string"
]);
} catch (ValidationException $exception) {
$errorMessage =
$exception->validator->getMessageBag()->getMessages();
}
}
陣列資料也是差不多的寫法,假設我們要更新多篇文章,request body 如:
{
"userId": 10,
"posts": [
{
"id": 3,
"title": "IT 鐵人第三天",
"content": null,
},
{
"id": 8,
"title": "Day 09. Request 超方便驗證幫手 - Validation",
"content": null,
}
// ...
]
}
陣列的驗證寫法和巢狀資料一樣,在於要加一個「*」作為辨識:
function edit(Request $request)
{
try {
$validResult = $request->validate([
"userId" => "required|integer|exists:users,id",
// 可以解釋為: posts 的「每一個」的 id
"posts.*.id" => "required|integer|exists:posts,id",
"posts.*.title" => "nullable|string",
"posts.*.content" => "nullable|string"
]);
} catch (ValidationException $exception) {
$errorMessage =
$exception->validator->getMessageBag()->getMessages();
}
}
有了 validation 之後讓 request 資料的驗證變得更加簡單也好維護...,等等! 那如果有些 request 的欄位驗證和邏輯都相同,如果有需要修改的時候,一個個 function 都要檢查也太累了,有沒有集中管理的辦法? 沒問題! 在明天 FormRequest
的介紹中我們會進一步說明,並且利用套件讓驗證規則可以同步用於前端開發。