服務(wù) ―― 驗(yàn)證

2018-02-24 15:39 更新

服務(wù) —— 驗(yàn)證

1、簡介

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

2、快速入門

要學(xué)習(xí)Laravel強(qiáng)大的驗(yàn)證特性,讓我們先看一個完整的驗(yàn)證表單并返回錯誤信息給用戶的例子。

2.1 定義路由

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

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

當(dāng)然,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)
    {
        // 驗(yàn)證并存儲博客文章...
    }
}

2.3 編寫驗(yàn)證邏輯

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

validate方法接收一個HTTP請求輸入數(shù)據(jù)和驗(yàn)證規(guī)則,如果驗(yàn)證規(guī)則通過,代碼將會繼續(xù)往下執(zhí)行;然而,如果驗(yàn)證失敗,將會拋出一個異常,相應(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',
    ]);

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

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

2.3.1 嵌套屬性注意事項(xiàng)

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

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

2.4 顯示驗(yàn)證錯誤信息

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

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

所以,在我們的例子中,驗(yà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請求&驗(yàn)證

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

3、其它驗(yàn)證方法

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

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

<?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ù)是需要驗(yàn)證的數(shù)據(jù),第二個參數(shù)是要應(yīng)用到數(shù)據(jù)上的驗(yàn)證規(guī)則。

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

3.1.1 命名錯誤包

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

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

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

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

3.1.2 驗(yàn)證鉤子之后

驗(yàn)證器允許你在驗(yàn)證完成后添加回調(diào),這種機(jī)制允許你輕松執(zhí)行更多驗(yàn)證,甚至添加更多錯誤信息到消息集合。使用驗(yàn)證器實(shí)例上的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 表單請求驗(yàn)證

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

php artisan make:request StoreBlogPostRequest

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

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

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

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

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

3.2.1 認(rèn)證表單請求

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

/**
 * 判斷請求用戶是否經(jīng)過認(rèn)證
 *
 * @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í)行。

如果你計(jì)劃在應(yīng)用的其他部分包含認(rèn)證邏輯,只需在authorize方法中簡單返回true即可:

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

3.2.2 自定義一次性錯誤格式

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

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

4、處理錯誤信息

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

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

要獲取指定字段的第一條錯誤信息,可以使用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 自定義錯誤信息

如果需要的話,你可以使用自定義錯誤信息替代默認(rèn)的,有多種方法來指定自定義信息。首先,你可以傳遞自定義信息作為第三方參數(shù)給Validator::make方法:

$messages = [
    'required' => 'The :attribute field is required.',
];

$validator = Validator::make($input, $rules, $messages);

在本例中,:attribute占位符將會被驗(yàn)證時(shí)實(shí)際的字段名替換,你還可以在驗(yàn)證消息中使用其他占位符,例如:

$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 為給定屬性指定自定義信息

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

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

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

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

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

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

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

accepted

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

active_url

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

after:date

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

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

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

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

alpha

該字段必須是字母

alpha_dash

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

alpha_num

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

array

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

before:date

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

between:min,max

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

boolean

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

confirmed

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

date

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

date_format:format

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

different:field

驗(yàn)證字段必須是一個和指定字段不同的值

digits:value

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

digits_between:min,max

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

email

驗(yàn)證字段必須是格式化的電子郵件地址

exists:table.column

驗(yàn)證字段必須存在于指定數(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

驗(yàn)證文件必須是圖片(jpeg、png、bmp、gif或者svg)

in:foo,bar…

驗(yàn)證字段值必須在給定的列表中

integer

驗(yàn)證字段必須是整型

ip

驗(yàn)證字段必須是IP地址

max:value

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

mimes:foo,bar,…

驗(yàn)證文件的MIMIE類型必須是該規(guī)則列出的擴(kuò)展類型中的一個

MIMIE規(guī)則的基本使用:

'photo' => 'mimes:jpeg,bmp,png'

min:value

驗(yàn)證字段的最小值,和字符串、數(shù)值、文件字段的size規(guī)則一起使用

not_in:foo,bar,…

驗(yàn)證字段值不在給定列表中

numeric

驗(yàn)證字段必須是數(shù)值

regex:pattern

驗(yàn)證字段必須匹配給定正則表達(dá)式

注意:使用regex模式時(shí),規(guī)則必須放在數(shù)組中,而不能使用管道分隔符,尤其是正則表達(dá)式中使用管道符號時(shí)。

required

驗(yàn)證字段時(shí)必須的

required_if:anotherfield,value,…

驗(yàn)證字段在另一個字段等于指定值value時(shí)是必須的

required_with:foo,bar,…

驗(yàn)證字段只有在任一其它指定字段存在的話才是必須的

required_with_all:foo,bar,…

驗(yàn)證字段只有在所有指定字段存在的情況下才是必須的

required_without:foo,bar,…

驗(yàn)證字段只有當(dāng)任一指定字段不存在的情況下才是必須的

required_without_all:foo,bar,…

驗(yàn)證字段只有當(dāng)所有指定字段不存在的情況下才是必須的

same:field

給定字段和驗(yàn)證字段必須匹配

size:value

驗(yàn)證字段必須有和給定值相value匹配的尺寸,對字符串而言,value是相應(yīng)的字符數(shù)目;對數(shù)值而言,value是給定整型值;對文件而言,value是相應(yīng)的文件字節(jié)數(shù)

string

驗(yàn)證字段必須是字符串

timezone

驗(yàn)證字符必須是基于PHP函數(shù)timezone_identifiers_list的有效時(shí)區(qū)標(biāo)識

unique:table,column,except,idColumn

驗(yàn)證字段在給定數(shù)據(jù)表上必須是唯一的,如果不指定column選項(xiàng),字段名將作為默認(rèn)column。

指定自定義列名:

'email' => 'unique:users,email_address'

自定義數(shù)據(jù)庫連接

有時(shí)候,你可能需要自定義驗(yàn)證器生成的數(shù)據(jù)庫連接,正如上面所看到的,設(shè)置unique:users作為驗(yàn)證規(guī)則將會使用默認(rèn)數(shù)據(jù)庫連接來查詢數(shù)據(jù)庫。要覆蓋默認(rèn)連接,在數(shù)據(jù)表名后使用”.“指定連接:

'email' => 'unique:connection.users,email_address'

強(qiáng)制一個唯一規(guī)則來忽略給定ID:

有時(shí)候,你可能希望在唯一檢查時(shí)忽略給定ID,例如,考慮一個包含用戶名、郵箱地址和位置的”更新屬性“界面,當(dāng)然,你將會驗(yàn)證郵箱地址是唯一的,然而,如果用戶只改變用戶名字段而并沒有改變郵箱字段,你不想要因?yàn)橛脩粢呀?jīng)擁有該郵箱地址而拋出驗(yàn)證錯誤,你只想要在用戶提供的郵箱已經(jīng)被別人使用的情況下才拋出驗(yàn)證錯誤,要告訴唯一規(guī)則忽略用戶ID,可以傳遞ID作為第三個參數(shù):

'email' => 'unique:users,email_address,'.$user->id

添加額外的where子句:

還可以指定更多條件給where子句:

'email' => 'unique:users,email_address,NULL,id,account_id,1'

url

驗(yàn)證字段必須是基于PHP函數(shù)filter_var過濾的的有效URL

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

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

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

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

復(fù)雜條件驗(yàn)證

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

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

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

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

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

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

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

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

Laravel提供了多種有用的驗(yàn)證規(guī)則;然而,你可能還是想要指定一些自己的驗(yàn)證規(guī)則。注冊驗(yàn)證規(guī)則的一種方法是使用Validator門面的extend方法。讓我們在服務(wù)提供者中使用這種方法來注冊一個自定義的驗(yàn)證規(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()
    {
        //
    }
}

自定義驗(yàn)證器閉包接收三個參數(shù):要驗(yàn)證的屬性名稱,屬性值和傳遞給規(guī)則的參數(shù)數(shù)組。

你還可以傳遞類和方法到extend方法而不是閉包:

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

定義錯誤信息

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

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

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

/**
 * 啟動應(yīng)用服務(wù)
 *
 * @return void
 */
public function boot(){
    Validator::extend(...);
    Validator::replacer('foo', function($message, $attribute, $rule, $parameters) {
        return str_replace(...);
    });
}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號