# 表單驗證
Laravel 提供了幾種不同的方法來驗證傳入應用程序的數據。默認情況下,Laravel 的控制器基類使用`ValidatesRequests`Trait,它提供了一種方便的方法去使用各種強大的驗證規則來驗證傳入的 HTTP 請求。
## 快速驗證
要了解 Laravel 強大的驗證功能,讓我們看一個驗證表單并將錯誤消息顯示回給用戶的完整示例。
### 定義路由
首先,讓我們假設在`routes/web.php`文件中定義了下面這些路由:
~~~php
Route::get('post/create', 'PostController@create');
Route::post('post', 'PostController@store');
~~~
顯然,`GET`路由會顯示一個供用戶創建一個新的博客帖子的表單,而 POST 路由會將新的博客文章存儲在數據庫中。
### 創建路由器
下面讓我們一起來看看處理這些路由的控制器,`store`方法暫時留空。
~~~php
<?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)
{
// 驗證并存儲博客文章...
}
}
~~~
### 編寫驗證器邏輯
現在我們開始在`store`方法中編寫邏輯來驗證新的博客文章。為此,我們將使用`Illuminate\Http\Request`對象提供的`validate`方法 。如果驗證通過,代碼就可以正常的運行。如果驗證失敗,則會拋出異常,并自動將對應的錯誤響應返回給用戶。在典型的 HTTP 請求的情況下,會生成一個重定向響應,而對于 AJAX 請求則會發送 JSON 響應。
讓我們接著回到`store`方法來深入理解`validate`方法:
~~~php
/**
* 保存一篇新的博客文章。
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$validatedData = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// 博客文章驗證通過
}
~~~
如你所見,我們將所需的驗證規則傳遞至`validate`方法中。另外再提醒一次,如果驗證失敗,會自動生成一個對應的響應。如果驗證通過,那我們的控制器將會繼續正常運行。
#### 首次驗證失敗后停止運行
如果你希望在某個屬性第一次驗證失敗后停止運行驗證規則,你需要附加`bail`規則到該屬性:
~~~php
$request->validate([
'title' => 'bail|required|unique:posts|max:255',
'body' => 'required',
]);
~~~
在這個例子中,如果`title`字段沒有通過`unique`規則,那么程序就不會繼續檢查`max`規則。規則會按照分配的順序來驗證。
#### 關于數組數據的注意實現
如果你的 HTTP 請求包含一個 「嵌套」 參數(即數組),那你可以在驗證規則中通過 「點」 語法來指定這些參數。
~~~php
$request->validate([
'title' => 'required|unique:posts|max:255',
'author.name' => 'required',
'author.description' => 'required',
]);
~~~
### 顯示驗證錯誤信息
如果傳入的請求參數未通過給定的驗證規則呢?正如前面所提到的,Laravel 會自動把用戶重定向到之前的位置。另外,所有的驗證錯誤信息會被自動[存儲到`session`中](https://laravel-china.org/docs/laravel/5.7/session#flash-data)。
重申一次,我們不必在`GET`路由中將錯誤消息顯式綁定到視圖。因為 Lavarel 會檢查在 Session 數據中的錯誤信息,并自動將其綁定到視圖(如果這個視圖文件存在)。而其中的變量`$errors`是`Illuminate\Support\MessageBag`的一個實例。要獲取關于這個對象的更多信息,請[查閱這個文檔](https://laravel-china.org/docs/laravel/5.7/validation/2262#working-with-error-messages).
> {tip}`$errors`變量被`Web`中間件組提供的`Illuminate\View\Middleware\ShareErrorsFromSession`中間件綁定到視圖中。**當這個中間件被應用后,在你的視圖中就可以獲取到`$error`變量**, 可以使一直假定`$errors`變量存在并且可以安全地使用。
在上面的例子中,當驗證失敗的時候,用戶將會被重定向到控制器的`create`方法,使我們能在視圖中顯示錯誤信息:
~~~php
<!-- /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
<!-- 創建文章表單 -->
~~~
### 關于可選字段的注意事項
默認情況下,在 Laravel 應用的全局中間件堆棧`App\Http\Kernel`類中包含了`TrimStrings`和`ConvertEmptyStringsToNull`中間件。因此,如果你不希望驗證程序將`null`值視為無效的話,那就需要將「可選」的請求字段標記為`nullable`,舉個例子:
~~~php
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
~~~
在這個例子里,我們指定`publish_at`字段可以為`null`或者一個有效的日期格式。如果`nullable`的修飾詞沒有被添加到規則定義中,驗證器會認為`null`是一個無效的日期格式。
#### AJAX 請求 & 驗證
在這個例子中,我們使用傳統的表單將數據發送到應用程序。但實際情況中,很多程序都會使用 AJAX 來發送請求。當我們對 AJAX 的請求中使用`validate`方法時,Laravel 并不會生成一個重定向響應,而是會生成一個包含所有驗證錯誤信息的 JSON 響應。這個 JSON 響應會包含一個 HTTP 狀態碼 422 被發送出去。
## 驗證表單請求
### 創建表單請求驗證
面對更復雜的驗證情境中,你可以創建一個「表單請求」來處理更為復雜的邏輯。表單請求是包含驗證邏輯的自定義請求類。可使用 Artisan 命令`make:request`來創建表單請求類:
~~~php
php artisan make:request StoreBlogPost
~~~
新生成的類保存在`app/Http/Requests`目錄下。如果這個目錄不存在,運行`make:request`命令時它會被創建出來。讓我們添加一些驗證規則到`rules`方法中:
~~~php
/**
* 獲取適用于請求的驗證規則。
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
~~~
> {tip} 你可以向`rules`方法傳入所需的任何依賴項。他們會自動被 Laravel 提供的[服務容器](https://laravel-china.org/docs/laravel/5.7/container)自動解析。
驗證規則是如何運行的呢?你所需要做的就是在控制器方法中類型提示傳入的請求。在調用控制器方法之前驗證傳入的表單請求,這意味著你不需要在控制器中寫任何驗證邏輯:
~~~php
/**
* 存儲傳入的博客文章。
*
* @param StoreBlogPost $request
* @return Response
*/
public function store(StoreBlogPost $request)
{
// 傳入的請求通過驗證...
// 獲取通過驗證的數據...
$validated = $request->validated();
}
~~~
如果驗證失敗,就會生成一個讓用戶返回到先前的位置的重定向響應。這些錯誤也會被閃存到`session`中,以便這些錯誤都可以在頁面中顯示出來。如果傳入的請求是 AJAX,會向用戶返回具有 422 狀態代碼和驗證錯誤信息的 JSON 數據的 HTTP 響應。
#### 添加表單請求后鉤子
如果你想在表單請求「之后」添加鉤子,可以使用`withValidator`方法。這個方法接收一個完整的驗證構造器,允許你在驗證結果返回之前調用任何方法:
~~~php
/**
* 配置驗證器實例。
*
* @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`方法。在這個方法中,你可以檢查經過身份驗證的用戶確定其是否具有更新給定資源的權限。比方說,你可以判斷用戶是否擁有更新文章評論的權限:
~~~php
/**
* 判斷用戶是否有權限做出此請求。
*
* @return bool
*/
public function authorize()
{
$comment = Comment::find($this->route('comment'));
return $comment && $this->user()->can('update', $comment);
}
~~~
由于所有的表單請求都是繼承了 Laravel 中的請求基類,所以我們可以使用`user`方法去獲取當前認證登錄的用戶。同時請注意上述例子中對`route`方法的調用。這個方法允許你在被調用的路由上獲取其定義的 URI 參數,譬如下面例子中的`{comment}`參數:
~~~php
Route::post('comment/{comment}');
~~~
如果`authorize`方法返回`false`,則會自動返回一個包含 403 狀態碼的 HTTP 響應,也不會運行控制器的方法。
如果你打算在應用程序的其它部分處理授權邏輯,只需從 authorize 方法返回 true :
~~~php
/**
* 判斷用戶是否有權限進行此請求。
*
* @return bool
*/
public function authorize()
{
return true;
}
~~~
> {tip} 你可以向`authorize`方法傳入所需的任何依賴項。他們會自動被 Laravel 提供的[服務容器](https://laravel-china.org/docs/laravel/5.7/container)自動解析。
### 自定義錯誤消息
你可以通過重寫表單請求的`messages`方法來自定義錯誤消息。此方法應該如下所示返回屬性/規則對數組及其對應錯誤消息:
~~~php
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages()
{
return [
'title.required' => 'A title is required',
'body.required' => 'A message is required',
];
}
~~~
## 手動創建驗證器
如果你不想在請求上使用`validate`方法,你可以通過`Validator`[facade](https://laravel-china.org/docs/laravel/5.7/facades)手動創建一個驗證器示例。
用`Validator`[facade](https://laravel-china.org/docs/laravel/5.7/facades)上的`make`方法創建一個驗證器示例:
~~~php
<?php
namespace App\Http\Controllers;
use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class PostController extends Controller
{
/**
* Store a new blog post.
*
* @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...
}
}
~~~
傳給`make`方法的第一個參數是需要驗證的數據。第二個參數則是該數據的驗證規則。
如果驗證失敗,則可以使用`withErrors`方法把錯誤消息閃存到 Session 。使用這個方法進行重定向后,`$errors`變量會自動和視圖共享,你可以把這些消息顯示給用戶。`withErrors`方法接收驗證器、`MessageBag`或 PHP`Array`。
### 自動重定向
如果你想手動創建驗證器實例,又想使用`validates`方法提供的自動重定向,那么你可以在現有的驗證器示例上調用`validate`方法。如果驗證失敗,用戶將會自動重定向。在 AJAX 請求中,則會返回 JSON 格式的響應。
~~~php
Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
])->validate();
~~~
### 命名錯誤包
如果你一個頁面中有多個表單,你可以通過命名錯誤包來檢索特定表單的錯誤消息。只需給`withErrors`方法傳遞一個名字作為第二個參數
~~~php
return redirect('register')
->withErrors($validator, 'login');
~~~
然后你就可以從`$errors`變量中獲取指定表單的錯誤消息:
~~~php
{{ $errors->login->first('email') }}
~~~
### 驗證后鉤子
驗證器還允許你添加在驗證成功之后允許的回調函數,以便你進行下一步的驗證,甚至在消息集合中添加更多的錯誤消息。使用它只需在驗證實例上使用`after`方法:
~~~php
$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`方法:
~~~php
$errors = $validator->errors();
echo $errors->first('email');
~~~
#### 查看特定字段的所有錯誤消息
如果你需要獲取指定字段的所有錯誤消息的數組,則可以使用`get`方法:
~~~php
foreach ($errors->get('email') as $message) {
//
}
~~~
如果要驗證表單的數組字段,你可以使用`*`來獲取每個數組元素的所有錯誤消息:
~~~php
foreach ($errors->get('attachments.*') as $message) {
//
}
~~~
#### 查看所有字段的所有錯誤消息
如果你想要得到所有字段的所有錯誤消息,可以使用`all`方法:
~~~php
foreach ($errors->all() as $message) {
//
}
~~~
#### 判斷特定字段是否含有錯誤消息
`has`方法可以被用來判斷給定的字段是否存在錯誤信息:
~~~php
if ($errors->has('email')) {
//
}
~~~
### 自定義錯誤消息
如果有需要的話,你也可以使用自定義錯誤消息取代默認值進行驗證。有幾種方法可以指定自定義消息。首先,你可以將自定義消息作為第三個參數傳遞給`Validator::make`方法:
~~~php
$messages = [
'required' => 'The :attribute field is required.',
];
$validator = Validator::make($input, $rules, $messages);
~~~
在這個例子中,`:attribute`占位符會被驗證字段的實際名稱取代。除此之外,你還可以在驗證消息中使用其它占位符。例如:
~~~php
$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',
];
~~~
#### 為給定屬性指定自定義消息
有時候你可能只想為特定的字段自定義錯誤消息。只需在屬性名稱后使用「點」語法來指定驗證的規則即可:
~~~php
$messages = [
'email.required' => 'We need to know your e-mail address!',
];
~~~
#### 在語言文件中指定自定義消息
在大多數情況下,您可能會在語言文件中指定自定義消息,而不是直接將它們傳遞給`Validator`。為此,需要把你的消息放置于`resources/lang/xx/validation.php`語言文件內的`custom`數組中。
~~~php
'custom' => [
'email' => [
'required' => 'We need to know your e-mail address!',
],
],
~~~
#### 在語言文件中指定自定義屬性
如果你希望將驗證消息的`:attribute`部分替換為自定義屬性名稱,你可以在`resources/lang/xx/validation.php`語言文件的`attributes`數組中指定自定義名稱:
~~~php
'attributes' => [
'email' => 'email address',
],
~~~
## 可用驗證規則
以下是所有可用驗證規則及其功能的列表:
[Accepted](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-accepted)
[Active URL](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-active-url)
[After (Date)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-after)
[After Or Equal (Date)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-after-or-equal)
[Alpha](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-alpha)
[Alpha Dash](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-alpha-dash)
[Alpha Numeric](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-alpha-num)
[Array](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-array)
[Bail](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-bail)
[Before (Date)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-before)
[Before Or Equal (Date)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-before-or-equal)
[Between](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-between)
[Boolean](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-boolean)
[Confirmed](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-confirmed)
[Date](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-date)
[Date Equals](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-date-equals)
[Date Format](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-date-format)
[Different](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-different)
[Digits](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-digits)
[Digits Between](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-digits-between)
[Dimensions (Image Files)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-dimensions)
[Distinct](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-distinct)
[E-Mail](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-email)
[Exists (Database)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-exists)
[File](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-file)
[Filled](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-filled)
[Greater Than](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-gt)
[Greater Than Or Equal](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-gte)
[Image (File)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-image)
[In](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-in)
[In Array](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-in-array)
[Integer](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-integer)
[IP Address](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-ip)
[JSON](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-json)
[Less Than](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-lt)
[Less Than Or Equal](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-lte)
[Max](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-max)
[MIME Types](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-mimetypes)
[MIME Type By File Extension](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-mimes)
[Min](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-min)
[Not In](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-not-in)
[Not Regex](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-not-regex)
[Nullable](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-nullable)
[Numeric](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-numeric)
[Present](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-present)
[Regular Expression](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-regex)
[Required](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required)
[Required If](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required-if)
[Required Unless](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required-unless)
[Required With](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required-with)
[Required With All](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required-with-all)
[Required Without](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required-without)
[Required Without All](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required-without-all)
[Same](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-same)
[Size](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-size)
[String](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-string)
[Timezone](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-timezone)
[Unique (Database)](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-unique)
[URL](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-url)
#### accepted
驗證字段必須是*yes*,*on*,*1*,或*true*。這在確認「服務條款」是否同意時相當有用。
#### active\_url
根據 PHP函數`dns_get_record`,驗證字段必須具有有效的A或AAAA記錄。
#### after:*date*
驗證字段必須是給定日期之后的值。日期值將傳遞到 PHP函數`strtotime`:
~~~php
'start_date' => 'required|date|after:tomorrow'
~~~
您可以指定另一個要與日期進行比較的字段,而不是傳遞要由`strtotime`處理的日期字符串:
~~~php
'finish_date' => 'required|date|after:start_date'
~~~
#### after\_or\_equal:*date*
驗證字段必須是在給定日期之后或與此日期相同的值。更多信息,請參閱[after](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-after)規則。
#### alpha
驗證字段必須完全由字母構成。
#### alpha\_dash
驗證字段可能包含字母、數字,以及破折號 ( - ) 和下劃線 ( \_ )。
#### alpha\_num
驗證字段必須是完全是字母、數字。
#### array
驗證的字段必須是一個 PHP 數組。
#### bail
在第一次驗證失敗后停止運行驗證規則。
#### before:*date*
驗證字段必須是在給定日期之前。這個日期值將會被傳遞給 PHP 的`strtotime`函數來計算。
#### before\_or\_equal:*date*
驗證字段必須是在給定日期之前或與之相同的日期。這個日期值將會被傳遞給 PHP 的`strtotime`函數來計算。
#### between:*min*,*max*
驗證字段的大小必須在給定的*min*和*max*之間。字符串、數字、數組和文件的計算方式都使用[`size`](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-size)方法。
#### boolean
驗證的字段必須可以轉換為 Boolean類型。 可接受的輸入為`true`,`false`,`1`,`0`,`"1"`, 和`"0"`。
#### confirmed
驗證字段必須具有匹配字段`foo_confirmation`。例如,驗證字段為`password`,輸入中必須存在與之匹配的`password_confirmation`字段。
#### date
根據 PHP函數`strtotime`,驗證字段必須是有效的日期。
#### date\_equals:*date*
驗證字段必須等于給定日期。日期將傳遞到 PHP 函數`strtotime`。
#### date\_format:*format*
驗證字段必須匹配給定的日期格式。當驗證某個字段的時候,你應該**只使用**`date`或者`date_format`,而不是同時使用。
#### different:*field*
驗證字段必須具有與*field*不同的值。
#### digits:*value*
驗證字段必須為*numeric*,且必須具有\_value\_的確切長度。
#### digits\_between:*min*,*max*
驗證的字段的長度必須在給定的*min*和*max*之間。
#### dimensions
驗證中的文件必須是圖片,并且符合指定的規則約束:
~~~php
'avatar' => 'dimensions:min_width=100,min_height=200'
~~~
可用的約束為:*min\_width*,*max\_width*,*min\_height*,*max\_height*,*width*,*height*,*ratio*.
*ratio*限制應該表示為寬度除以高度。 這可以通過使用像`3/2`的表達式 或者一個浮點數,像`1.5`來指定:
~~~php
'avatar' => 'dimensions:ratio=3/2'
~~~
由于此規則需要多個參數,你可以使用`Rule::dimensions`方法流暢地構造規則:
~~~php
use Illuminate\Validation\Rule;
Validator::make($data, [
'avatar' => [
'required',
Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2),
],
]);
~~~
#### distinct
當驗證數組時,驗證字段不得包含任何重復值。
~~~php
'foo.*.id' => 'distinct'
~~~
#### email
驗證字段必須為正確格式的電子郵件地址。
#### exists:*table*,*column*
驗證字段必須存在于給定的數據庫表中。
#### Exists 規則基本用法
~~~php
'state' => 'exists:states'
~~~
如果未指定`column`選項,則將使用字段名稱。
#### 指定自定義的表字段
~~~php
'state' => 'exists:states,abbreviation'
~~~
有時,你可能需要指定要用于「exists」查詢的特定數據庫連接。你可以使用「點」語法將連接名稱添加到表名稱前來完成此操作:
~~~php
'email' => 'exists:connection.staff,email'
~~~
如果您想自定義驗證規則執行的查詢,您可以使用`Rule`類來流暢地定義規則。在下面的例子中,我們還以數組的形式指定驗證規則,而不是使用`|`字符來分隔它們:
~~~php
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`方法流暢地構造規則:
~~~php
use Illuminate\Validation\Rule;
Validator::make($data, [
'zones' => [
'required',
Rule::in(['first-zone', 'second-zone']),
],
]);
~~~
#### in\_array:*anotherfield*
驗證的字段必須存在于另一個字段?*anotherfield*?的值中。
#### integer
驗證的字段必須是整數。
#### ip
驗證的字段必須是 IP 地址。
#### ipv4
驗證的字段必須是 IPv4 地址。
#### ipv6
驗證的字段必須是 IPv6 地址。
#### json
驗證的字段必須是有效的 JSON 字符串。
#### lt:*field*
驗證中的字段必須小于給定的字段。這兩個字段必須是相同的類型。字符串、數值、數組和文件大小的計算方式與`size`方法進行評估。
#### lte:*field*
驗證中的字段必須小于或等于給定的*字段*。這兩個字段必須是相同的類型。字符串、數值、數組和文件大小的計算方式與`size`方法進行評估。
#### max:*value*
驗證中的字段必須小于或等于?*value*。字符串、數字、數組或是文件大小的計算方式都用?[`size`](https://laravel-china.org/docs/laravel/5.7/validation/1372#rule-size)?方法進行評估。
#### mimetypes:*text/plain*,...
驗證的文件必須與給定 MIME 類型之一匹配:
~~~php
'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime'
~~~
要確定上傳文件的 MIME 類型,會讀取文件的內容來判斷 MIME 類型,這可能與客戶端提供的 MIME 類型不同。
#### mimes:*foo*,*bar*,...
驗證的文件必須具有與列出的其中一個擴展名相對應的 MIME 類型。
#### MIME 規則基本用法
~~~php
'photo' => 'mimes:jpeg,bmp,png'
~~~
即使你可能只需要驗證指定擴展名,但此規則實際上會驗證文件的 MIME 類型,其通過讀取文件的內容以猜測它的 MIME 類型。
可以在以下鏈接中找到完整的 MIME 類型列表及其相應的擴展名:
[https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types](https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types)
#### min:*value*
驗證中的字段必須具有最小值。字符串、數字、數組或是文件大小的計算方式都用?[`size`](https://laravel-china.org/docs/laravel/5.6/validation/1372#rule-size)?方法進行評估。
#### not\_in:*foo*,*bar*,...
驗證的字段不能包含在給定的值列表中。`Rule::notIn`?方法可以用來構建規則:
~~~php
use Illuminate\Validation\Rule;
Validator::make($data, [
'toppings' => [
'required',
Rule::notIn(['sprinkles', 'cherries']),
],
]);
~~~
#### not\_regex:*pattern*
驗證中的字段必須與給定的正則表達式不匹配。
**注意:**當使用`regex`/`not_regex`模式時,可能需要在數組中指定規則,而不是使用管道分隔符,尤其是在正則表達式包含管道字符的情況下。
#### nullable
驗證的字段可以為?`null`。這在驗證基本數據類型時特別有用,例如可以包含空值的字符串和整數。
#### numeric
驗證的字段必須是數字。
#### present
驗證的字段必須存在于輸入數據中,但可以為空。
#### regex:*pattern*
驗證的字段必須與給定的正則表達式匹配。
**注意:**?當使用?`regex`?規則時,你必須使用數組,而不是使用?`|`?分隔符,特別是如果正則表達式包含?`|`?字符。
#### required
驗證的字段必須存在于輸入數據中,而不是空。如果滿足以下條件之一,則字段被視為「空」:
* 值為?`null`。
* 值為空字符串。
* 值為空數組或空?`Countable`?對象。
* 值為無路徑的上傳文件。
#### required\_if:*anotherfield*,*value*,...
如果?*anotherfield*?字段等于任一?*value*,驗證的字段必須出現且不為空 。
#### 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 對應給定的整數值。對于數組,size 對應數組的`count`值。對于文件,size 對應文件大小(單位kb)。
#### string
驗證的字段必須是一個字符串。如果允許這個字段為`null`,需要給這個字段分配`nullable`規則。
#### timezone
驗證的字段必須是一個基于 PHP 函數`timezone_identifiers_list`的有效時區標識。
#### unique:*table*,*column*,*except*,*idColumn*
驗證的字段在給定的數據庫表中必須是唯一的。如果沒有指定`column`,將會使用字段本身的名稱。
**指定自定義字段**
~~~php
'email' => 'unique:users,email_address'
~~~
**自定義數據庫連接**
有時,你可能需要為驗證程序創建的數據庫查詢設置自定義連接。上面的例子中,將`unique:users`設置為驗證規則,等于使用默認數據庫連接來查詢數據庫。如果要對其進行修改,請使用「點」方法指定連接和表名:
~~~php
'email' => 'unique:connection.users,email_address'
~~~
**強迫 Unique 規則忽略指定 ID :**
有時,你可能希望在進行字段唯一性驗證時忽略指定 ID 。例如, 在「更新個人資料」頁面會包含用戶名、郵箱和地點。這時你會想要驗證更新的 E-mail 值是否唯一。如果用戶僅更改了用戶名字段而沒有改 E-mail 字段,就不需要拋出驗證錯誤,因為此用戶已經是這個 E-mail 的擁有者了。
使用`Rule`類定義規則來指示驗證器忽略用戶的 ID。這個例子中通過數組來指定驗證規則,而不是使用 | 字符來分隔:
~~~php
use Illuminate\Validation\Rule;
Validator::make($data, [
'email' => [
'required',
Rule::unique('users')->ignore($user->id),
],
]);
~~~
如果你的數據表使用的主鍵名稱不是`id`,那就在調用`ignore`方法時指定字段的名稱:
~~~php
'email' => Rule::unique('users')->ignore($user->id, 'user_id')
~~~
**增加額外的 Where 語句:**
你也可以通過`where`方法指定額外的查詢條件。例如, 我們添加`account_id`為`1`的約束:
~~~php
'email' => Rule::unique('users')->where(function ($query) {
return $query->where('account_id', 1);
})
~~~
#### url
驗證的字段必須是有效的 URL。
## 按條件增加規則
#### 存在時則驗證
在某些情況下,**只有**在該字段存在于數組中時, 才可以對字段執行驗證檢查。可通過增加`sometimes`到規則列表來實現:
~~~php
$v = Validator::make($data, [
'email' => 'sometimes|required|email',
]);
~~~
在上面的例子中,`email`字段只有在`$data`數組中存在才會被驗證。
> {tip} 如果你嘗試驗證應該始終存在但可能為空的字段,請查閱[可選字段的注意事項](https://laravel-china.org/docs/laravel/5.7/validation/2262#a-note-on-optional-fields)
#### 復雜的條件驗證
有時候你可能需要增加基于更復雜的條件邏輯的驗證規則。例如,你可以希望某個指定字段在另一個字段的值超過 100 時才為必填。或者當某個指定字段存在時,另外兩個字段才能具有給定的值。增加這樣的驗證條件并不難。首先,使用 靜態規則 創建一個`Validator`實例:
~~~php
$v = Validator::make($data, [
'email' => 'required|email',
'games' => 'required|numeric',
]);
~~~
假設我們有一個專為游戲收藏家所設計的網頁應用程序。如果游戲收藏家收藏超過一百款游戲,我們會希望他們來說明下為什么他們會擁有這么多游戲。比如說他們有可能經營了一家游戲分銷商店,或者只是為了享受收集的樂趣。為了在特定條件下加入此驗證需求,可以在`Validator`實例中使用`sometimes`方法。
~~~php
$v->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 100;
});
~~~
傳入`sometimes`方法的第一個參數是要用來驗證的字段名稱。第二個參數是我們想使用的驗證規則。`閉包`作為第三個參數傳入,如果其返回`true`, 則額外的規則就會被加入。這個方法可以輕松地創建復雜的條件驗證。你甚至可以一次對多個字段增加條件驗證:
~~~php
$v->sometimes(['reason', 'cost'], 'required', function ($input) {
return $input->games >= 100;
});
~~~
> {tip} 傳入`閉包`的`$input`參數是`Illuminate\Support\Fluent`的一個實例,可用來訪問你的輸入或文件對象。
## 驗證數組
驗證表單的輸入為數組的字段也不難。你可以使用 「點」方法來驗證數組中的屬性。例如,如果傳入的 HTTP 請求中包含`photos[profile]`字段, 可以如下驗證:
~~~php
$validator = Validator::make($request->all(), [
'photos.profile' => 'required|image',
]);
~~~
你也可以驗證數組中的每個元素。例如,要驗證指定數組輸入字段中的每一個 email 是唯一的,可以這么做:
~~~php
$validator = Validator::make($request->all(), [
'person.*.email' => 'email|unique:users',
'person.*.first_name' => 'required_with:person.*.last_name',
]);
~~~
同理,你可以在語言文件定義驗證信息時使用`*`字符,為基于數組的字段使用單個驗證消息:
~~~php
'custom' => [
'person.*.email' => [
'unique' => 'Each person must have a unique e-mail address',
]
],
~~~
## 自定義驗證規則
### 使用規則對象
Laravel 提供了許多有用的驗證規則;同時也支持自定義規則。注冊自定義驗證規則的方法之一,就是使用規則對象。可以使用 Artisan 命令`make:rule`來生成新的規則對象。接下來,讓我們用這個命令生成一個驗證字符串是大寫的規則。Laravel 會將新的規則存放在`app/Rules`目錄中:
~~~php
php artisan make:rule Uppercase
~~~
一旦創建了規則,我們就可以定義它的行為。規則對象包含兩個方法:`passes`和`message`。`passes`方法接收屬性值和名稱,并根據屬性值是否符合規則而返回`true`或`false`。`message`方法應返回驗證失敗時應使用的驗證錯誤消息:
~~~php
<?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.';
}
}
~~~
當然, 如果你希望從翻譯文件中返回一個錯誤消息,你可以從`message`方法中調用輔助函數`trans`:
~~~php
/**
* 獲取驗證錯誤消息。
*
* @return string
*/
public function message()
{
return trans('validation.uppercase');
}
~~~
一旦規則對象被定義好后,你可以通過將規則對象的實例和其他驗證規則一起來傳遞給驗證器:
~~~php
use App\Rules\Uppercase;
$request->validate([
'name' => ['required', 'string', new Uppercase],
]);
~~~
### 使用閉包
如果你在應用程序中只需要一次自定義規則的功能,則可以使用閉包而不是規則對象。閉包接收屬性的名稱,屬性的值如果驗證失敗則應該使用回調中的`$fail`:
~~~php
$validator = Validator::make($request->all(), [
'title' => [
'required',
'max:255',
function($attribute, $value, $fail) {
if ($value === 'foo') {
return $fail($attribute.' is invalid.');
}
},
],
]);
~~~
### 使用擴展
注冊自定義的驗證規則的另一種方法是使用`Validator`[facade](https://laravel-china.org/docs/laravel/5.7/facades)中的`extend`方法。讓我們在[服務容器](https://laravel-china.org/docs/laravel/5.7/providers)中使用這個方法來注冊自定義驗證規則:
~~~php
<?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()
{
//
}
}
~~~
自定義的驗證閉包接收四個參數:要被驗證的屬性名稱`$attribute`、屬性的值`$value`、傳入驗證規則的參數數組`$parameters`、以及`Validator`實列。
除了使用閉包,你也可以傳入類和方法到`extend`方法中:
~~~php
Validator::extend('foo', 'FooValidator@validate');
~~~
#### 定義錯誤消息
你還需要為自定義規則定義錯誤信息。你可以使用內聯自定義消息數組或者在驗證語言文件中添加條目來實現這一功能。 消息應該被放到數組的第一位, 而不是在只用于存放屬性指定錯誤信息的`custom`數組內:
~~~php
"foo" => "Your input was invalid!",
"accepted" => "The :attribute must be accepted.",
// 其余的驗證錯誤消息...
~~~
當創建一個自定義驗證規則時,你可能有時候需要為錯誤信息定義自定義占位符。可以通過創建自定義驗證器然后調用`Validator`門面上的`replacer`方法。你可以在[服務容器](https://laravel-china.org/docs/laravel/5.7/providers)的`boot`方法中執行如下操作:
~~~php
/**
* 啟動應用程序。
*
* @return void
*/
public function boot()
{
Validator::extend(...);
Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) {
return str_replace(...);
});
}
~~~
#### 隱式擴展
默認情況下, 當所要驗證的屬性不存在或包含由[`required`](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-required)規則定義的空值時,那么正常的驗證規則,包括自定義擴展將不會執行。 例如,[`unique`](https://laravel-china.org/docs/laravel/5.7/validation/2262#rule-unique)規則將不會檢驗`null`值:
~~~php
$rules = ['name' => 'unique'];
$input = ['name' => null];
Validator::make($input, $rules)->passes(); // true
~~~
如果要求即使為空時也要驗證屬性,則必須要暗示屬性是必須的。要創建這樣一個「隱式」擴展, 可以使用`Validator::extendImplicit()`方法:
~~~php
Validator::extendImplicit('foo', function ($attribute, $value, $parameters, $validator) {
return $value == 'foo';
});
~~~
> {note} 「隱式」擴展只暗示該屬性是必需的。至于它到底是缺失的還是空值這取決于你。