鍍金池/ 教程/ PHP/ 驗證
門面
Laravel Homestead
安裝及配置
測試
HTTP 中間件
加密
升級指南
幫助函數(shù)
應(yīng)用目錄結(jié)構(gòu)
集合
新手入門指南-簡單任務(wù)管理系統(tǒng)
任務(wù)調(diào)度
查詢構(gòu)建器
視圖
驗證
Laravel Cashier(訂購&支付&發(fā)票)
本地化
隊列
調(diào)整器
分頁
文件系統(tǒng)/云存儲
貢獻代碼
哈希
HTTP 控制器
緩存
遷移
HTTP 請求
Laravel Elixir
發(fā)行版本說明
Envoy 任務(wù)運行器(SSH任務(wù))
序列化
Session
起步
帶用戶功能的任務(wù)管理系統(tǒng)
起步
用戶授權(quán)
郵件
事件
填充數(shù)據(jù)
HTTP 路由
服務(wù)提供者
Blade 模板引擎
包開發(fā)
用戶認證
Artisan 控制臺
HTTP 響應(yīng)
集合
服務(wù)容器
關(guān)聯(lián)關(guān)系
一次請求的生命周期
契約
Redis
錯誤&日志

驗證

1、簡介

Laravel 提供了多種方法來驗證應(yīng)用輸入數(shù)據(jù)。默認情況下,Laravel 的控制器基類使用 ValidatesRequests trait,該 trait 提供了便利的方法通過各種功能強大的驗證規(guī)則來驗證輸入的 HTTP 請求。

2、快速入門

要學習 Laravel 強大的驗證特性,讓我們先看一個完整的驗證表單并返回錯誤信息給用戶的例子。

2.1 定義路由

首先,我們假定在 app/Http/routes.php 文件中包含如下路由:

// 顯示創(chuàng)建博客文章表單...
Route::get('post/create', 'PostController@create');
// 存儲新的博客文章...
Route::post('post', 'PostController@store');

當然,GET 路由為用戶顯示了一個創(chuàng)建新的博客文章的表單,POST 路由將新的博客文章存儲到數(shù)據(jù)庫。

2.2 創(chuàng)建控制器

接下來,讓我們看一個處理這些路由的簡單控制器示例。我們先將 store 方法留空:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller{
    /**
     * 顯示創(chuàng)建新的博客文章的表單
     *
     * @return Response
     */
    public function create()
    {
        return view('post.create');
    }

    /**
     * 存儲新的博客文章
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request)
    {
        // 驗證并存儲博客文章...
    }
}

2.3 編寫驗證邏輯

現(xiàn)在我們準備用驗證新博客文章輸入的邏輯填充 store 方法。如果你檢查應(yīng)用的控制器基類(App\Http\Controllers\Controller),你會發(fā)現(xiàn)該類使用了 ValidatesRequests trait,這個 trait 在所有控制器中提供了一個便利的 validate 方法。

validate 方法接收一個 HTTP 請求輸入數(shù)據(jù)和驗證規(guī)則,如果驗證規(guī)則通過,代碼將會繼續(xù)往下執(zhí)行;然而,如果驗證失敗,將會拋出一個異常,相應(yīng)的錯誤響應(yīng)也會自動發(fā)送給用戶。在一個傳統(tǒng)的 HTTP 請求案例中,將會生成一個重定向響應(yīng),如果是 AJAX 請求則會返回一個 JSON 響應(yīng)。 要更好的理解 validate 方法,讓我們回到 store 方法:

/**
 * 存儲博客文章
 *
 * @param  Request  $request
 * @return Response
 */
public function store(Request $request){
    $this->validate($request, [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // 驗證通過,存儲到數(shù)據(jù)庫...
}

正如你所看到的,我們只是傳遞輸入的 HTTP 請求和期望的驗證規(guī)則到 validate 方法,在強調(diào)一次,如果驗證失敗,相應(yīng)的響應(yīng)會自動生成。如果驗證通過,控制器將會繼續(xù)正常執(zhí)行。

2.3.1 嵌套屬性注意事項

如果 HTTP 請求中包含“嵌套”參數(shù),可以使用“.”在驗證規(guī)則中指定它們:

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

2.4 顯示驗證錯誤信息

那么,如果請求輸入?yún)?shù)沒有通過給定驗證規(guī)則怎么辦?正如前面所提到的,Laravel 將會自動將用戶重定向回上一個位置。此外,所有驗證錯誤信息會自動一次性存放到 session。

注意我們并沒有在 GET 路由中明確綁定錯誤信息到視圖。這是因為 Laravel 總是從 session 數(shù)據(jù)中檢查錯誤信息,而且如果有的話會自動將其綁定到視圖。所以,值得注意的是每次請求的所有視圖中總是存在一個$errors 變量,從而允許你在視圖中方便而又安全地使用。$errors 變量是的一個 Illuminate\Support\MessageBag 實例。想要了解更多關(guān)于該對象的信息,查看其文檔。

所以,在我們的例子中,驗證失敗的話用戶將會被重定向到控制器的 create 方法,從而允許我們在視圖中顯示錯誤信息:

<!-- /resources/views/post/create.blade.php -->

<h1>Create Post</h1>

@if (count($errors) > 0)
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<!-- Create Post Form -->

2.5 AJAX 請求&驗證

在這個例子中,我們使用傳統(tǒng)的表單來發(fā)送數(shù)據(jù)到應(yīng)用。然而,很多應(yīng)用使用 AJAX 請求。在 AJAX 請求中使用 validate 方法時,Laravel 不會生成重定向響應(yīng)。取而代之的,Laravel 生成一個包含驗證錯誤信息的 JSON 響應(yīng)。該 JSON 響應(yīng)會帶上一個 HTTP 狀態(tài)碼422

3、其它驗證方法

3.1 手動創(chuàng)建驗證器

如果你不想使用 ValidatesRequests trait 的 validate 方法,可以使用 Validator門面手動創(chuàng)建一個驗證器實例,該門面上的 make 方法用于生成一個新的驗證器實例:

<?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();
        }

        // 存儲博客文章...
    }
}

傳遞給 make 方法的第一個參數(shù)是需要驗證的數(shù)據(jù),第二個參數(shù)是要應(yīng)用到數(shù)據(jù)上的驗證規(guī)則。

檢查請求是夠通過驗證后,可以使用 withErrors 方法將錯誤數(shù)據(jù)一次性存放到 session,使用該方法時,$errors 變量重定向后自動在視圖間共享,從而允許你輕松將其顯示給用戶,withErrors 方法接收一個驗證器、或者一個 MessageBag,又或者一個 PHP 數(shù)組。

3.1.1 命名錯誤包

如果你在單個頁面上有多個表單,可能需要命名 MessageBag,從而允許你為指定表單獲取錯誤信息。只需要傳遞名稱作為第二個參數(shù)給 withErrors 即可:

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

然后你就可以從$errors 變量中訪問命名的 MessageBag 實例:

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

3.1.2 驗證鉤子之后

驗證器允許你在驗證完成后添加回調(diào),這種機制允許你輕松執(zhí)行更多驗證,甚至添加更多錯誤信息到消息集合。使用驗證器實例上的 after 方法即可:

$validator = Validator::make(...);

$validator->after(function($validator) {
    if ($this->somethingElseIsInvalid()) {
        $validator->errors()->add('field', 'Something is wrong with this field!');
    }
});

if ($validator->fails()) {
    //
}

3.2 表單請求驗證

對于更復(fù)雜的驗證場景,你可能想要創(chuàng)建一個“表單請求”。表單請求是包含驗證邏輯的自定義請求類,要創(chuàng)建表單驗證類,可以使用 Artisan 命令 make:request

php artisan make:request StoreBlogPostRequest

生成的類位于 app/Http/Requests 目錄下,接下來我們添加少許驗證規(guī)則到 rules方法:

/**
 * 獲取應(yīng)用到請求的驗證規(guī)則
 *
 * @return array
 */
public function rules(){
    return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ];
}

那么,驗證規(guī)則如何生效呢?你所要做的就是在控制器方法中類型提示該請求。表單輸入請求會在控制器方法被調(diào)用之前被驗證,這就是說你不需要將控制器和驗證邏輯雜糅在一起:

/**
 * 存儲輸入的博客文章
 *
 * @param  StoreBlogPostRequest  $request
 * @return Response
 */
public function store(StoreBlogPostRequest $request){
    // The incoming request is valid...
}

如果驗證失敗,重定向響應(yīng)會被生成并將用戶退回上一個位置,錯誤信息也會被一次性存儲到 session 以便在視圖中顯示。如果是 AJAX 請求,帶 422 狀態(tài)碼的 HTTP 響應(yīng)將會返回給用戶,該響應(yīng)數(shù)據(jù)中還包含了 JSON 格式的驗證錯誤信息。

3.2.1 認證表單請求

表單請求類還包含了一個authorize方法,你可以檢查認證用戶是否有資格更新指定資源。例如,如果用戶嘗試更新一個博客評論,那么他是否是評論的所有者呢?舉個例子:

/**
 * 判斷請求用戶是否經(jīng)過認證
 *
 * @return bool
 */
public function authorize(){
    $commentId = $this->route('comment');
    return Comment::where('id', $commentId)
                  ->where('user_id', Auth::id())->exists();
}

注意上面這個例子中對 route 方法的調(diào)用。該方法賦予用戶訪問被調(diào)用路由 URI 參數(shù)的權(quán)限,比如下面這個例子中的{comment}參數(shù):

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

如果 authorize 方法返回 false,一個包含403 狀態(tài)碼的 HTTP 響應(yīng)會自動返回而且控制器方法將不會被執(zhí)行。 如果你計劃在應(yīng)用的其他部分包含認證邏輯,只需在 authorize方法中簡單返回 true即可:

/**
 * 判斷請求用戶是否經(jīng)過認證
 *
 * @return bool
 */
public function authorize(){
    return true;
}

3.2.2 自定義一次性錯誤格式

如果你想要自定義驗證失敗時一次性存儲到 session 中驗證錯誤信息的格式,重寫請求基類(App\Http\Requests\Request)中的 formatErrors 方法即可。不要忘記在文件頂部導入 Illuminate\Contracts\Validation\Validator 類:

/**
 * {@inheritdoc}
 */
protected function formatErrors(Validator $validator){
    return $validator->errors()->all();
}

4、處理錯誤信息

調(diào)用 Validator 實例上的errors 方法之后,將會獲取一個 Illuminate\Support\MessageBag 實例,該實例中包含了多種處理錯誤信息的便利方法。

獲取某字段的第一條錯誤信息

要獲取指定字段的第一條錯誤信息,可以使用 first方法:

$messages = $validator->errors();
echo $messages->first('email');

獲取指定字段的所有錯誤信息

如果你想要簡單獲取指定字段的所有錯誤信息數(shù)組,使用 get 方法:

foreach ($messages->get('email') as $message) {
    //
}

獲取所有字段的所有錯誤信息

要獲取所有字段的所有錯誤信息,可以使用 all方法:

foreach ($messages->all() as $message) {
    //
}

判斷消息中是否存在某字段的錯誤信息

if ($messages->has('email')) {
    //
}

獲取指定格式的錯誤信息

echo $messages->first('email', '<p>:message</p>');

獲取指定格式的所有錯誤信息

foreach ($messages->all('<li>:message</li>') as $message) {
    //
}

4.1 自定義錯誤信息

如果需要的話,你可以使用自定義錯誤信息替代默認的,有多種方法來指定自定義信息。首先,你可以傳遞自定義信息作為第三方參數(shù)給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',
];

4.1.1 為給定屬性指定自定義信息

有時候你可能只想為特定字段指定自定義錯誤信息,可以通過”.”來實現(xiàn),首先指定屬性名,然后是規(guī)則:

$messages = [
    'email.required' => 'We need to know your e-mail address!',
];

4.1.2 在語言文件中指定自定義消息

在很多案例中,你可能想要在語言文件中指定屬性特定自定義消息而不是將它們直接傳遞給 Validator。要實現(xiàn)這個,添加消息到 resources/lang/xx/validation.php 語言文件的 custom 數(shù)組:

'custom' => [
    'email' => [
        'required' => 'We need to know your e-mail address!',
    ],
],

5、有效驗證規(guī)則

下面是有效規(guī)則及其函數(shù)列表:

  • Accepted
  • Active URL
  • After (Date)
  • Alpha
  • Alpha Dash
  • Alpha Numeric
  • Array
  • Before (Date)
  • Between
  • Boolean
  • Confirmed
  • Date
  • Date Format
  • Different
  • Digits
  • Digits Between
  • E-Mail
  • Exists (Database)
  • Image (File)
  • In
  • Integer
  • IP Address
  • Max
  • MIME Types (File)
  • Min
  • Not In
  • Numeric
  • Regular Expression
  • Required
  • Required If
  • Required With
  • Required With All
  • Required Without
  • Required Without All
  • Same
  • Size
  • String
  • Timezone
  • Unique (Database)
  • URL

accepted

在驗證中該字段的值必須是 yes、on、1true,這在“同意服務(wù)協(xié)議”時很有用。

active_url

該字段必須是一個基于 PHP 函數(shù) checkdnsrr的有效 URL

after:date

該字段必須是給定日期后的一個值,日期將會通過 PHP 函數(shù) strtotime 傳遞:

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

你可以指定另外一個比較字段而不是使用 strtotime 驗證傳遞的日期字符串:

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

alpha

該字段必須是字母

alpha_dash

該字段可以包含字母和數(shù)字,以及破折號和下劃線

alpha_num

該字段必須是字母或數(shù)字

array

該字段必須是 PHP 數(shù)組

before:date

驗證字段必須是指定日期之前的一個數(shù)值,該日期將會傳遞給 PHP strtotime函數(shù)。

between:min,max

驗證字段尺寸在給定的最小值和最大值之間,字符串、數(shù)值和文件都可以使用該規(guī)則

boolean

驗證字段必須可以被轉(zhuǎn)化為 boolean,接收 true, false, 1,0,"1", 和 "0" 等輸入。

confirmed

驗證字段必須有一個匹配字段 foo_confirmation,例如,如果驗證字段是 password,必須輸入一個與之匹配的 password_confirmation 字段

date

驗證字段必須是一個基于 PHP strtotime函數(shù)的有效日期

date_format:format

驗證字段必須匹配指定格式,該格式將使用 PHP 函數(shù) date_parse_from_format 進行驗證。你應(yīng)該在驗證字段時使用 datedate_format

different:field

驗證字段必須是一個和指定字段不同的值

digits:value

驗證字段必須是數(shù)字且長度為 value 指定的值

digits_between:min,max

驗證字段數(shù)值長度必須介于最小值和最大值之間

email

驗證字段必須是格式化的電子郵件地址

exists:table.column

驗證字段必須存在于指定數(shù)據(jù)表

  • 基本使用:
'state' => 'exists:states'
  • 指定自定義列名:
'state' => 'exists:states,abbreviation'

還可以添加更多查詢條件到 where 查詢子句:

'email' => 'exists:staff,email,account_id,1'
  • 傳遞 NULL 作為 where 子句的值將會判斷數(shù)據(jù)庫值是否為 NULL:
'email' => 'exists:staff,email,deleted_at,NULL'
  • image驗證文件必須是圖片(jpeg、png、bmp、gif 或者 svg)

  • in:foo,bar…驗證字段值必須在給定的列表中

  • integer驗證字段必須是整型

  • ip驗證字段必須是 IP 地址

  • max:value驗證字段必須小于等于最大值,和字符串、數(shù)值、文件字段的 size 規(guī)則一起使用

  • mimes:foo,bar,…驗證文件的 MIMIE 類型必須是該規(guī)則列出的擴展類型中的一個 MIMIE 規(guī)則的基本使用:
'photo' => 'mimes:jpeg,bmp,png'
  • min:value 驗證字段的最小值,和字符串、數(shù)值、文件字段的 size 規(guī)則一起使用

  • not_in:foo,bar,… 驗證字段值不在給定列表中

  • numeric 驗證字段必須是數(shù)值

  • regex:pattern 驗證字段必須匹配給定正則表達式 注意:使用 regex模式時,規(guī)則必須放在數(shù)組中,而不能使用管道分隔符,尤其是正則表達式中使用管道符號時。

  • required 驗證字段時必須的

  • required_if:anotherfield,value,… 驗證字段在另一個字段等于指定值 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 是相應(yīng)的字符數(shù)目;對數(shù)值而言,value 是給定整型值;對文件而言,value 是相應(yīng)的文件字節(jié)數(shù)

  • string 驗證字段必須是字符串

  • timezone 驗證字符必須是基于 PHP 函數(shù) timezone_identifiers_list 的有效時區(qū)標識

  • unique:table,column,except,idColumn 驗證字段在給定數(shù)據(jù)表上必須是唯一的,如果不指定 column 選項,字段名將作為默認 column。 指定自定義列名:
'email' => 'unique:users,email_address'
  • 自定義數(shù)據(jù)庫連接 有時候,你可能需要自定義驗證器生成的數(shù)據(jù)庫連接,正如上面所看到的,設(shè)置 unique:users 作為驗證規(guī)則將會使用默認數(shù)據(jù)庫連接來查詢數(shù)據(jù)庫。要覆蓋默認連接,在數(shù)據(jù)表名后使用”.“指定連接:
'email' => 'unique:connection.users,email_address'
  • 強制一個唯一規(guī)則來忽略給定 ID: 有時候,你可能希望在唯一檢查時忽略給定 ID,例如,考慮一個包含用戶名、郵箱地址和位置的”更新屬性“界面,當然,你將會驗證郵箱地址是唯一的,然而,如果用戶只改變用戶名字段而并沒有改變郵箱字段,你不想要因為用戶已經(jīng)擁有該郵箱地址而拋出驗證錯誤,你只想要在用戶提供的郵箱已經(jīng)被別人使用的情況下才拋出驗證錯誤,要告訴唯一規(guī)則忽略用戶 ID,可以傳遞 ID 作為第三個參數(shù):
'email' => 'unique:users,email_address,'.$user->id
  • 添加額外的 where 子句: 還可以指定更多條件給 where 子句:
'email' => 'unique:users,email_address,NULL,id,account_id,1'
  • url 驗證字段必須是基于 PHP 函數(shù) filter_var 過濾的的有效 URL

6、添加條件規(guī)則

在某些場景下,你可能想要只有某個字段存在的情況下運行驗證檢查,要快速完成這個,添加 sometimes 規(guī)則到規(guī)則列表:

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

在上例中,email 字段只有存在于$data 數(shù)組時才會被驗證。

復(fù)雜條件驗證 有時候你可能想要基于更復(fù)雜的條件邏輯添加驗證規(guī)則。例如,你可能想要只有在另一個字段值大于 100 時才要求一個給定字段是必須的,或者,你可能需要只有當另一個字段存在時兩個字段才都有給定值。添加這個驗證規(guī)則并不是一件頭疼的事。首先,創(chuàng)建一個永遠不會改變的靜態(tài)規(guī)則到 Validator 實例:

$v = Validator::make($data, [
    'email' => 'required|email',
    'games' => 'required|numeric',
]);

讓我們假定我們的 web 應(yīng)用服務(wù)于游戲收集者。如果一個游戲收集者注冊了我們的應(yīng)用并擁有超過 100 個游戲,我們想要他們解釋為什么他們會有這么多游戲,例如,也許他們在運營一個游戲二手店,又或者他們只是喜歡收集。要添加這種條件,我們可以使用 Validator 實例上的 sometimes 方法:

$v->sometimes('reason', 'required|max:500', function($input) {
    return $input->games >= 100;
});

傳遞給 sometimes 方法的第一個參數(shù)是我們需要有條件驗證的名稱字段,第二個參數(shù)是我們想要添加的規(guī)則,如果作為第三個參數(shù)的閉包返回true,規(guī)則被添加。該方法讓構(gòu)建復(fù)雜條件驗證變得簡單,你甚至可以一次為多個字段添加條件驗證:

$v->sometimes(['reason', 'cost'], 'required', function($input) {
    return $input->games >= 100;
});

注意:傳遞給閉包的$input 參數(shù)是 Illuminate\Support\Fluent 的一個實例,可用于訪問輸入和文件。

7、自定義驗證規(guī)則

Laravel 提供了多種有用的驗證規(guī)則;然而,你可能還是想要指定一些自己的驗證規(guī)則。注冊驗證規(guī)則的一種方法是使用Validator門面的 extend 方法。讓我們在服務(wù)提供者中使用這種方法來注冊一個自定義的驗證規(guī)則:

<?php

namespace App\Providers;

use Validator;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider{
    /**
     * 啟動應(yīng)用服務(wù)
     *
     * @return void
     */
    public function boot()
    {
        Validator::extend('foo', function($attribute, $value, $parameters) {
            return $value == 'foo';
        });
    }

    /**
     * 注冊服務(wù)提供者
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

自定義驗證器閉包接收三個參數(shù):要驗證的屬性名稱,屬性值和傳遞給規(guī)則的參數(shù)數(shù)組。 你還可以傳遞類和方法到 extend 方法而不是閉包:

Validator::extend('foo', 'FooValidator@validate');

定義錯誤信息 你還需要為自定義規(guī)則定義錯誤信息。你可以使用內(nèi)聯(lián)自定義消息數(shù)組或者在驗證語言文件中添加條目來實現(xiàn)這一目的。消息應(yīng)該被放到數(shù)組的第一維,而不是在只用于存放屬性指定錯誤信息的 custom 數(shù)組內(nèi):

"foo" => "Your input was invalid!",
"accepted" => "The :attribute must be accepted.",
// 驗證錯誤信息其它部分...

當創(chuàng)建一個自定義驗證規(guī)則時,你可能有時候需要為錯誤信息定義自定義占位符,可以通過創(chuàng)建自定義驗證器然后調(diào)用Validator門面上的replacer方法來實現(xiàn)??梢栽诜?wù)提供者的boot 方法中編寫代碼:

/**
 * 啟動應(yīng)用服務(wù)
 *
 * @return void
 */
public function boot(){
    Validator::extend(...);
    Validator::replacer('foo', function($message, $attribute, $rule, $parameters) {
        return str_replace(...);
    });
}
上一篇:起步下一篇:HTTP 控制器