老死机带你深入理解 Laravel 之验证器下
前言
在上一篇博文《老死机带你深入理解 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 协议》,转载必须注明作者和本文链接
推荐文章: