[TOC]
# 表單驗證
## 簡介
默認情況下,Laravel 的控制器基類使用`ValidatesRequests`Trait,它提供了一種方便的方法去使用各種強大的驗證規則來驗證傳入的 HTTP 請求。
## 快速驗證
### 定義路由
```
Route::get('post/create', 'PostController@create');
Route::post('post', 'PostController@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');
}
/**
* 保存一篇新的博客文章。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// 驗證并存儲博客文章...
}
}
```
### 編寫驗證器邏輯
WEB頁面請求失敗生成一個重定向響應,而 AJAX 請求則會發送 JSON 響應,JSON 響應會包含一個 HTTP 狀態碼 422。
```
/**
* 保存一篇新的博客文章。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// 博客文章驗證通過
}
```
#### 首次驗證失敗后停止運行
附加 bail 規則,在第一次驗證失敗后停止驗證該屬性的其他驗證規則。
```
$request->validate([
'title' => 'bail|required|unique:posts|max:255',
'body' => 'required',
]);
```
#### 驗證數組數據
在驗證規則中通過「點」語法來指定這些參數
```
$request->validate([
'title' => 'required|unique:posts|max:255',
'author.name' => 'required',
'author.description' => 'required',
]);
```
### 顯示驗證錯誤信息
請求未通過驗證,Laravel 會自動把用戶重定向到之前的位置。
所有的驗證錯誤信息會被自動存儲到 session 中。
變量 $errors 是 Illuminate\\Support\\MessageBag 實例
> 注意:`$errors`變量被`Web`中間件組提供的`Illuminate\View\Middleware\ShareErrorsFromSession`中間件綁定到視圖中。**當這個中間件被應用后,在你的視圖中就可以獲取到`$error`變量**, 可以使一直假定`$errors`變量存在并且可以安全地使用。
```
// 無需顯示綁定錯誤,直接在重定向的模板顯示 session 內的錯誤信息
<!-- /resources/views/post/create.blade.php -->
<h1>創建文章</h1>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- 創建文章表單 -->
```
Blade 模板方法
```
@error('title')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
```
### 允許 null 驗證
默認情況下,在 Laravel 應用的全局中間件堆棧`App\Http\Kernel`類中包含了`TrimStrings`和`ConvertEmptyStringsToNull`中間件。因此,如果你不希望驗證程序將`null`值視為無效的話,那就需要將「可選」的請求字段標記為`nullable`。
```
// 允許null值有效,添加驗證規則 nullable
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
```
## 驗證表單請求
### 創建表單請求驗證
```
// 創建表單請求驗證,生成的目錄 app/Http/Requests
$ php artisan make:request StoreBlogPost
```
#### 添加驗證規則
```
/**
* 獲取適用于請求的驗證規則。
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
```
#### 在控制器中使用
```
use App\Http\Requests\StoreBlogPost;
/**
* 存儲傳入的博客文章。
*
* @param StoreBlogPost $request
* @return Response
*/
public function store(StoreBlogPost $request)
{
// 傳入的請求通過驗證...
// 獲取通過驗證的數據...
$validated = $request->validated();
}
```
#### 添加表單請求后鉤子
允許在驗證結果返回之前調用任何方法
```
/**
* 配置驗證器實例。
*
* @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!');
}
});
}
```
### 表單請求授權驗證
可以檢查經過身份驗證的用戶確定其是否具有更新給定資源的權限。
```
/**
* 判斷用戶是否有權限做出此請求。
*
* @return bool
*/
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;
}
```
### 自定義錯誤消息
```
/**
* 獲取已定義驗證規則的錯誤消息。
*
* @return array
*/
public function messages()
{
return [
'title.required' => 'A title is required',
'body.required' => 'A message is required',
];
}
```
### 自定義驗證屬性
```
/**
* 獲取驗證錯誤的自定義屬性。
*
* @return array
*/
public function attributes()
{
return [
'email' => '郵箱地址',
];
}
```
## 手動創建驗證器
通過 Validator facade 上的`make`方法手動創建驗證器實例,傳給`make`方法的第一個參數是需要驗證的數據,第二個參數則是該數據的驗證規則。
如果驗證失敗,則可以使用`withErrors`方法把錯誤消息閃存到 Session 。使用這個方法進行重定向后,`$errors`變量會自動和視圖共享,你可以把這些消息顯示給用戶。`withErrors`方法接收驗證器、`MessageBag`或 PHP`Array`。
```
<?php
namespace App\Http\Controllers;
use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* 保存一篇新的博客文章。
*
* @param Request $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();
}
// Store the blog post...
}
}
```
### 自動重定向
如果驗證失敗,用戶將會自動重定向。在 AJAX 請求中,則會返回 JSON 格式的響應。
```
Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
])->validate();
```
### 命名錯誤包
如果一個頁面中有多個表單,可以通過給`withErrors`方法傳遞第二個參數命名錯誤包來檢索特定表單的錯誤消息。
```
// 然后你就可以從`$errors`變量中獲取指定表單的錯誤消息:
return redirect('register')
->withErrors($validator, 'login');
{{ $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`類的一個實例。
#### 查看特定字段的第一個錯誤信息
```
$errors = $validator->errors();
echo $errors->first('email');
```
#### 查看特定字段的所有錯誤消息
```
foreach ($errors->get('email') as $message) {
//
}
// 如果要驗證表單的數組字段,可以使用`*`來獲取每個數組元素的所有錯誤消息
foreach ($errors->get('attachments.*') as $message) {
//
}
```
#### 查看所有字段的所有錯誤消息
```
foreach ($errors->all() as $message) {
//
}
```
### 自定義錯誤消息
可以使用自定義錯誤消息取代默認值進行驗證。有幾種方法可以指定自定義消息。首先,你可以將自定義消息作為第三個參數傳遞給`Validator::make`方法:
```
// `:attribute`占位符會被驗證字段的實際名稱取代。
$messages = [
'required' => 'The :attribute field is required.',
];
$validator = Validator::make($input, $rules, $messages);
// 還可以在驗證消息中使用其它占位符。
$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 e-mail address!',
];
```
#### 在語言文件中指定自定義消息
在 resources/lang/xx/validation.php 語言文件的 custom 數組中自定義消息
```
'custom' => [
'email' => [
'required' => 'We need to know your e-mail address!',
],
],
```
#### 在語言文件中指定自定義屬性
在 resources/lang/xx/validation.php 語言文件的 attributes 數組自定義名稱
```
'attributes' => [
'email' => 'email address',
],
```
#### 在語言文件中指定自定義值
有時可能需要將驗證消息的`:value`占位符替換為值的自定義文字。
```
// 模板添加
<input type="hidden" name="payment_type" value="cc">
// 當payment type為cc時,credit card number 不能為空。
$request->validate([
'credit_card_number' => 'required_if:payment_type,cc'
]);
```
在 resources/lang/xx/validation.php 語言文件定義`values`數組
```
// 當payment type 為信用卡時,credit card number不能為空。
'values' => [
'payment_type' => [
'cc' => '信用卡'
],
],
```
## 可用驗證規則
[參考文檔](https://learnku.com/docs/laravel/5.8/validation/3899#c58a91)
## 按條件增加規則
#### 存在時則驗證
```
// email 字段只有在 $data 數組中存在才會被驗證 sometimes
$v = Validator::make($data, [
'email' => 'sometimes|required|email',
]);
// 驗證始終存在但可能為空的字段 nullable
$v = Validator::make($data, [
'publish_at' => 'nullable|date',
]);
```
#### 復雜的條件驗證
傳入`sometimes`方法的第一個參數是要用來驗證的字段名稱。第二個參數是我們想使用的驗證規則。`閉包`作為第三個參數傳入,如果其返回`true`, 則額外的規則就會被加入。
```
$v = Validator::make($data, [
'email' => 'required|email',
'games' => 'required|numeric',
]);
// 增加基于更復雜的條件邏輯的驗證規則
$v->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 100;
});
// 對多個字段增加條件驗證
$v->sometimes(['reason', 'cost'], 'required', function ($input) {
return $input->games >= 100;
});
```
> 注意:傳入閉包的參數 $input 是 Illuminate\Support\Fluent 實例,可用來訪問你的輸入或文件對象。
## 驗證數組
```
// 驗證 photos[profile]
$validator = Validator::make($request->all(), [
'photos.profile' => 'required|image',
]);
// 驗證指定數組輸入字段中的每一個 email 是唯一的
$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 e-mail address',
]
],
```
## 自定義驗證規則
### 使用規則對象
注冊自定義驗證規則,存放目錄`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
{
/**
* 判斷驗證規則是否通過。
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
return strtoupper($value) === $value;
}
/**
* 獲取驗證錯誤消息。
*
* @return string
*/
public function message()
{
return 'The :attribute must be uppercase.';
}
}
```
或者從語言文件中返回一個錯誤文本
```
public function message()
{
return trans('validation.uppercase');
}
```
使用自定義驗證規則
```
use App\Rules\Uppercase;
$request->validate([
'name' => ['required', 'string', new Uppercase],
]);
```
### 使用閉包
如果在應用程序中只需要一次自定義規則的功能,則可以使用閉包而不是規則對象。
```
$validator = Validator::make($request->all(), [
'title' => [
'required',
'max:255',
function ($attribute, $value, $fail) {
if ($value === 'foo') {
$fail($attribute.' is invalid.');
}
},
],
]);
```
### 使用擴展
注冊自定義的驗證規則的另一種方法是使用`Validator`facade 中的`extend`方法。需要在 [服務容器] 中使用這個方法來注冊自定義驗證規則:
```
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* 引導任何應用程序。
*
* @return void
*/
public function boot()
{
/**
* $attribute:要被驗證的屬性名稱
* $value:屬性的值
* $parameters :傳入驗證規則的參數數組
* $validator:Validator 實列
*/
Validator::extend('foo', function ($attribute, $value, $parameters, $validator) {
return $value == 'foo';
});
}
/**
* 注冊服務提供器。
*
* @return void
*/
public function register()
{
//
}
}
```
除了使用閉包,也可以傳入類和方法到`extend`方法中:
```
Validator::extend('foo', 'FooValidator@validate');
```
### 定義錯誤消息
可以使用內聯自定義消息數組或者在驗證語言文件中添加條目。
錯誤消息應該被放到數組的第一位,而不是在只用于存放屬性指定錯誤信息的`custom`數組內。
```
"foo" => "Your input was invalid!",
"accepted" => "The :attribute must be accepted.",
// 其余的驗證錯誤消息...
```
為錯誤信息定義自定義占位符
```
/**
* 啟動應用程序。
*
* @return void
*/
public function boot()
{
Validator::extend(...);
Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) {
return str_replace(...);
});
}
```
### 隱式擴展
默認情況下, 當所要驗證的屬性不存在或包含由[`required`](https://learnku.com/docs/laravel/5.8/validation/3899#rule-required)規則定義的空值時,那么正常的驗證規則,包括自定義擴展將不會執行。 例如,[`unique`](https://learnku.com/docs/laravel/5.8/validation/3899#rule-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';
});
```
- 入門指南
- 安裝
- 部署
- 基礎功能
- 路由
- 中間件
- CSRF 保護
- 控制器
- 請求
- 響應
- 視圖
- URL
- Session
- 表單驗證
- 錯誤
- 日志
- 前端開發
- Blade 模板
- 本地化
- 腳手架
- 編譯資源 Mix
- 安全相關
- 用戶認證
- API 認證
- 綜合話題
- 命令行
- 廣播
- 緩存
- 集合
- 事件
- 文件存儲
- 輔助函數
- 郵件發送
- 消息通知
- 擴展包開發
- 隊列
- 任務調度
- 數據庫
- 快速入門
- 查詢構造器
- 分頁
- 數據庫遷移
- 數據填充
- Redis
- Eloquent ORM
- 快速入門
- 速查表
- Artisan
- Auth
- Blade
- Cache
- Collection
- Composer
- Config
- Container
- Cookie
- DB
- Environment
- Event
- File
- Helper
- Input
- Lang
- Log
- Model
- Pagination
- Queue
- Redirect
- Request
- Response
- Route
- SSH
- Schema
- Security
- Session
- Storage
- String
- URL
- UnitTest
- Validation
- View