- 我們有時會碰到一個場景,比如買車票,需要進行查詢車票剩餘,這時候需要一個日期範圍,開始日期必須大於等於今天,結束日期必須大於等於開始日期。
Version
- Laravel 5.5.40
- PHP 7.1
開始之前,為方便檢視結果,在/Exceptions/Handler.php
檔案捕獲了異常
那麼我們開始做一個驗證吧:
- 新建一個
request
並注入到需要驗證控制器的方法
- 下圖圈出的紅色部分,需改成
return true
因為我們身份驗證一般不在這裡驗證
- 如上圖,
end_date
可以很簡單的通過start_date
來限制時間範圍,那麼start_date
該怎麼驗證呢?我本來想的是這樣的。
- 通過增加一個額外的欄位
curr_date
,然後start_date
通過這個欄位限制範圍。測試發現實際並不生效。直接進入了控制器列印的資料
- 除錯了一下,我在
IlluminateValidationConcerns、ValidatesAttributes::validateAfterOrEqual
方法跳到compareDates
方法,並嘗試列印了$this->getValue($parameters[0])
這時候的$paramters[0]
為curr_date
總是null
- 然後查閱了一下原始碼,知道了原因,直接說能正確驗證的方法,再說詳細的過程。
- 正確的方法是這樣的
- 重寫父類的
prepareForValidation
方法,之後再測試
- 這時候得到想要的驗證了
- 分析一下過程
- 能通過依賴注入例項化我們想要的物件,這一切都得感謝
Laravel
的IoC
容器 - 容器每次解析完新物件之後,總是會釋放一些繫結的事件
- 這些事件很多都是通過服務提供者來繫結的,我們直接檢視
config/app.php
裡有關表單驗證的服務提供者
- 我們可以看到當實現
ValidatesWhenResolved
物件的類在解析之後會自動呼叫validate
方法 - 回到新建的
IndexTicketRequest
類,沒有這個方法,去到父類,發現實現了ValidatesWhenResolved
,而validate
是在trait
的ValidatesWhenResolvedTrait
- 這個類找到了答案,在
validate
方法裡分三步主要的 -
$this->prepareForValidation()
在驗證之前的準備 - 新建一個驗證例項
- 開始驗證
- 之所以是需要在驗證之前設定
curr_date
,我們來看看新建驗證例項便知道答案
- 這裡有一個地方導致我們的問題出現,就是先呼叫了
$this->validationData()
方法拿到request
的資料,然後再通過$this->container->call([$this, `rules`])
拿到驗證規則,所以我們在rules
方法寫的自然不生效了,之後的驗證便無法繼續進行