老當機帶你深入理解 Laravel 之驗證器下

Dennis_Ritchie發表於2019-12-30

前言

在上一篇博文《老當機帶你深入理解 Laravel 之驗證器上(控制器引數解析)
》,我帶大家瞭解了Laravel是如何解析控制器方法的引數的,在今天的這篇部落格中,我會給大家講解laravel的驗證器到底是如何起作用的。

準備

這篇博文是接著上篇《老當機帶你深入理解 Laravel 之驗證器上(控制器引數解析)
》來的,所以你必須先讀這篇。

問題

在前面的分析中,我們看到Laravel會解析控制器方法的每一個引數,如果引數是類的例項,那麼Laravel會通過容器建立這個引數,這個之前已經分析過了,但是你有懷疑過沒有,驗證器啥時候起作用的呢?因為到現在為止,我們都沒看到哪個地方有呼叫過驗證器的程式碼。

臨時

問題就出在這裡:

Laravel

容器的make方法會呼叫Illuminate\Container\Container類的resolve方法:

Laravel

不熟悉容器的同學,請看看官方的文件,使用起來很簡單,我們進入到resolve方法中,這個方法的程式碼很多,但是和我們現在分析的程式碼有關的程式碼只有一行,就是下面這個哥:

Laravel

好了,在解釋這個方法fireResolvingCallbacks之前,我們還需要知道一個東西,Laravel在引導的時候,註冊了很多的ServiceProvider,關於ServiceProvider,請參考我之前寫的這篇《部落格:老司機帶你深入 Laravel 之 ServiceProvider 原理》。我說這個的原因是,Laravel此時註冊了一個Illuminate\Foundation\Providers\FormRequestServiceProvider的服務提供者,我們看他的boot方法:

Laravel

關於它的作用,我們先不管,我們看看容器的afterResolving方法:

Laravel

我們需要關心的是此時afterResolving方法的呼叫,結果儲存在了容器的afterResolvingCallbacks屬性中。好了,我們現在回到fireResolvingCallbacks方法中:

Laravel

我們進入到fireAfterResolvingCallbacks方法中:

Laravel

這裡的getCallbacksForType方法被呼叫:

Laravel

對於我們的DebugFormRequest來說,我們看它的定義:

Laravel

Illuminate\Foundation\Http\FormRequest的定義如下:

Laravel

Illuminate\Foundation\Http\FormRequest實現了ValidatesWhenResolved介面,還記得Illuminate\Foundation\Providers\FormRequestServiceProvider中的boot方法註冊了一個ValidatesWhenResolved::class,我再給大家貼出來:

Laravel

回到容器的getCallbacksForType方法中,因為我們的DebugFormRequest實現了ValidatesWhenResolved::class這個介面,所以它會被返回給呼叫者,返回到fireAfterResolvingCallbacks方法中,繼續呼叫fireCallbackArray方法:

Laravel

這段程式碼就很簡單了,遍歷所有符合條件的回撥方法,這裡我們的回撥就是ValidatesWhenResolved::class註冊的,我們看它的回撥方法,引數$resolvedDebugFormRequest類的物件,進入到回撥方法內部,呼叫DebugFormRequest類的validate方法,DebugFormRequest本身,包括它的父類都沒有實現validate方法,但是DebugFormRequest父類Illuminate\Foundation\Http\FormRequest使用到了ValidatesWhenResolvedTrait這個trait,如下所示:

臨時

ValidatesWhenResolvedTrait實現了validate方法,如下:

臨時

我們進入到validate方法中,繼續分析程式碼,prepareForValidation方法被設計為protected,所以在當前類中是空的,子類可以實現它,做任何操作,接下來呼叫getValidatorInstance方法,這個方法獲取一個驗證器例項,這個方法被Illuminate\Foundation\Http\FormRequest所覆蓋,我們看:

臨時

$this->container->make(ValidationFactory::class)返回的是Illuminate\Validation\Factory類的物件,關於getValidatorInstance方法的程式碼,我只分析預設驗證器的情況(另外一種可以自行分析):

臨時

首先validationData方法被呼叫,這個方法很簡單,直接呼叫$this->all(),這個方法是哪裡來的呢?我們知道FormRequest繼承自Illuminate\Http\RequestIlluminate\Http\Request的定義如下所示:

臨時

Illuminate\Http\Request使用到了多個trait,這其中就包括Concerns\InteractsWithInputall()方法就是它實現的,我並不打算分析這個方法,你只需要知道這個方法返回當前請求的所有資料(),無論是post請求還是get請求。

我們返回到Illuminate\Foundation\Http\FormRequest類的createDefaultValidator方法中,呼叫$this->container->call([$this, 'rules'])方法,rules方法就是我們自己實現的方法,這個大家應該都知道的,$this->messages()方法我們自己也是可以實現的,自定義錯誤提示資訊,$this->attributes()方法的作用,如果你不清楚,請檢視這個連結:Customizing The Validation Attributes,不過除了rules方法外,其它自定義方法都不是我們分析的重點。

createDefaultValidator方法的最後呼叫Illuminate\Validation\Factory類的make方法:

臨時

這個方法中,只有一行程式碼是關鍵,resolve方法被呼叫:

臨時

這裡直接返回一個Illuminate\Validation\Validator類的物件,好了,經過上面的分析,我們從Illuminate\Foundation\Http\FormRequest類的createDefaultValidator方法中返回,回到當前類的getValidatorInstance方法,呼叫如下程式碼:

臨時

這裡檢查我們是否實現了withValidator方法,你可以在這裡做一些你自己的驗證,關於這些細節問題,以後有時間再說,我現在這裡貼出Laravel官網對它的解釋和用法:

臨時

經過上面的分析,我們就可以從getValidatorInstance方法返回到ValidatesWhenResolvedTraitvalidate方法中:

臨時

呼叫passesAuthorization方法,類FormRequest實現了這個方法:

臨時

這個方法返回一個bool值,表示是否通過驗證,比如我的程式碼:

臨時

返回到ValidatesWhenResolvedTraitvalidate方法中,最關鍵的程式碼是$instance->passes()方法,這裡呼叫我們之前建立的Illuminate\Validation\Validator類的passes方法,這是整個驗證最關鍵的部分:

臨時

這裡的程式碼很簡單,就是遍歷所有的驗證規則,檢查是否通過驗證,關於規則如何驗證的,留給大家自己分析吧,這個很簡單。

臨時

Illuminate\Validation\Validator類的passes方法中返回之後,我們再一次回到ValidatesWhenResolvedTraitvalidate方法中,如果剛才的驗證失敗的話,那麼failedValidation方法會被呼叫,FormRequest類實現了這個方法,我們來看:

臨時

這裡直接丟擲了一個Illuminate\Validation\ValidationException類的異常,那麼你可能會好奇,這個異常怎麼處理的呢?下面我簡要的給大家分析哈(具體的後面會有專門的章節講解),看下圖:

臨時

我們專案的根目錄下面的app/Exceptions目錄下有個Handler.php檔案,這個檔案就是用來處理異常的,這個類繼承自Illuminate\Foundation\Exceptions\Handler類,這個類的render方法,如下所示:

臨時

這個異常的型別和驗證器丟擲的異常型別是一樣的,方法convertValidationExceptionToResponse也很簡單:

臨時

expectsJson檢查當前是不是Ajax請求,如果是的話,呼叫invalidJson方法:

臨時

總結

該文是驗證器原理系列的第二篇,希望你能夠理解,諸多細節,需要靠你自己去挖掘,如果你看不懂,請聯絡我,交流是成長的必要手段,在下有個QQ群,有興趣可以加下:

本作品採用《CC 協議》,轉載必須註明作者和本文連結

如果有不懂的地方,可以加我的qq:1174332406,或者是微信:itshardjs

相關文章