前言
在上一篇博文《老當機帶你深入理解 Laravel 之驗證器上(控制器引數解析)
》,我帶大家瞭解了Laravel是如何解析控制器方法的引數的,在今天的這篇部落格中,我會給大家講解laravel的驗證器到底是如何起作用的。
準備
這篇博文是接著上篇《老當機帶你深入理解 Laravel 之驗證器上(控制器引數解析)
》來的,所以你必須先讀這篇。
問題
在前面的分析中,我們看到Laravel會解析控制器方法的每一個引數,如果引數是類的例項,那麼Laravel會通過容器建立這個引數,這個之前已經分析過了,但是你有懷疑過沒有,驗證器啥時候起作用的呢?因為到現在為止,我們都沒看到哪個地方有呼叫過驗證器的程式碼。
問題就出在這裡:
容器的make
方法會呼叫Illuminate\Container\Container
類的resolve
方法:
不熟悉容器的同學,請看看官方的文件,使用起來很簡單,我們進入到resolve
方法中,這個方法的程式碼很多,但是和我們現在分析的程式碼有關的程式碼只有一行,就是下面這個哥:
好了,在解釋這個方法fireResolvingCallbacks
之前,我們還需要知道一個東西,Laravel在引導的時候,註冊了很多的ServiceProvider
,關於ServiceProvider
,請參考我之前寫的這篇《部落格:老司機帶你深入 Laravel 之 ServiceProvider 原理》。我說這個的原因是,Laravel此時註冊了一個Illuminate\Foundation\Providers\FormRequestServiceProvider
的服務提供者,我們看他的boot
方法:
關於它的作用,我們先不管,我們看看容器的afterResolving
方法:
我們需要關心的是此時afterResolving
方法的呼叫,結果儲存在了容器的afterResolvingCallbacks
屬性中。好了,我們現在回到fireResolvingCallbacks
方法中:
我們進入到fireAfterResolvingCallbacks
方法中:
這裡的getCallbacksForType
方法被呼叫:
對於我們的DebugFormRequest
來說,我們看它的定義:
類Illuminate\Foundation\Http\FormRequest
的定義如下:
類Illuminate\Foundation\Http\FormRequest
實現了ValidatesWhenResolved
介面,還記得Illuminate\Foundation\Providers\FormRequestServiceProvider
中的boot
方法註冊了一個ValidatesWhenResolved::class
,我再給大家貼出來:
回到容器的getCallbacksForType
方法中,因為我們的DebugFormRequest
實現了ValidatesWhenResolved::class
這個介面,所以它會被返回給呼叫者,返回到fireAfterResolvingCallbacks
方法中,繼續呼叫fireCallbackArray
方法:
這段程式碼就很簡單了,遍歷所有符合條件的回撥方法,這裡我們的回撥就是ValidatesWhenResolved::class
註冊的,我們看它的回撥方法,引數$resolved
是DebugFormRequest
類的物件,進入到回撥方法內部,呼叫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\Request
,Illuminate\Http\Request
的定義如下所示:
Illuminate\Http\Request
使用到了多個trait,這其中就包括Concerns\InteractsWithInput
,all()
方法就是它實現的,我並不打算分析這個方法,你只需要知道這個方法返回當前請求的所有資料(),無論是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
方法返回到ValidatesWhenResolvedTrait
的validate
方法中:
呼叫passesAuthorization
方法,類FormRequest
實現了這個方法:
這個方法返回一個bool值,表示是否通過驗證,比如我的程式碼:
返回到ValidatesWhenResolvedTrait
的validate
方法中,最關鍵的程式碼是$instance->passes()
方法,這裡呼叫我們之前建立的Illuminate\Validation\Validator
類的passes
方法,這是整個驗證最關鍵的部分:
這裡的程式碼很簡單,就是遍歷所有的驗證規則,檢查是否通過驗證,關於規則如何驗證的,留給大家自己分析吧,這個很簡單。
從Illuminate\Validation\Validator
類的passes
方法中返回之後,我們再一次回到ValidatesWhenResolvedTrait
的validate
方法中,如果剛才的驗證失敗的話,那麼failedValidation
方法會被呼叫,FormRequest
類實現了這個方法,我們來看:
這裡直接丟擲了一個Illuminate\Validation\ValidationException
類的異常,那麼你可能會好奇,這個異常怎麼處理的呢?下面我簡要的給大家分析哈(具體的後面會有專門的章節講解),看下圖:
我們專案的根目錄下面的app/Exceptions目錄下有個Handler.php
檔案,這個檔案就是用來處理異常的,這個類繼承自Illuminate\Foundation\Exceptions\Handler
類,這個類的render
方法,如下所示:
這個異常的型別和驗證器丟擲的異常型別是一樣的,方法convertValidationExceptionToResponse
也很簡單:
expectsJson
檢查當前是不是Ajax
請求,如果是的話,呼叫invalidJson
方法:
總結
該文是驗證器原理系列的第二篇,希望你能夠理解,諸多細節,需要靠你自己去挖掘,如果你看不懂,請聯絡我,交流是成長的必要手段,在下有個QQ群,有興趣可以加下:
本作品採用《CC 協議》,轉載必須註明作者和本文連結