3.2.3 - Laravel - 5.6 - Route - 路由配置文件加载mapWebRoutes方法解析
3.2.1 讲了group方法对系统中的所有路由加载成对象的大概流程。
3.2.2 讲了group方法中属性的合并(如果存在父group或者全局的group属性的规则)
这一节 细讲下-路由配置文件的加载过程-中的实现逻辑:
3.2.1 提到在路由加载配置文件web.php的时候,主要用到了这么一个方法mapWebRoutes
。
在这个方法中他用到了middleware方法和namespace方法。这两个方法是给Group添加属性的方法。如下:
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
这里首先 Route
是一个Facade调用。Facade返回Router对象。(注意:并不是Route对象)。
而在Router对象中不存在middleware和namespace方法。所以他会去调用魔术方法__call
方法来处理。
Router类中的__call
方法如下:
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
if ($method === 'middleware') {
return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
}
*.宏机制:先讲一下宏的机制便于后面理解。
简单说就是可以对一个类动态的添加外部方法,而这个方法并不用在类内部实现。在类不变的情况下添加方法。举例:
Route::macro('middleware', function(){
var_dump("test");
return "test for middleware";
});
在不更改Router类的前提下,对Router类添加了一个方法middleware
,方法体就是后面的闭包函数。
然后我们使用下面的代码就可以调用。但是我们并没有更改Router对象中的源代码。
Route::middleware();
1.好了 我们回到源码,这里第一步
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
首先查看有没有提前设置好的宏。如果存在就可以触发返回。不再执行下面的逻辑。
简单说,就是Router类触发的方法如果不在类中出现,那就去宏集合中查看是否提前设置了这个方法,然后触发。
这个地方告诉我们,我们是可以自己定义方法的,包括middleware 和 namespace
2.第二步:如果没有定义宏
机制下的自定义方法。继续执行。
if ($method === 'middleware') {
return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
如果call的方法是middleware
。(只针对middleware方法)
新建一个RouteRegistrar
对象后调用他的attribute方法把middleware
作为key,middleware的参数(这里是 web
)作为value存入RouteRegistrar
对象的attributes
数组中
is_array($parameters[0]) ? $parameters[0] : $parameters
针对middleware方法可以参数传入数组,参数也可以传入一个字符串。
然后我们可以去看下attribute方法了。
首先可以看到最后返回的是RouteRegistrar对象。(这个RouteRegistrar类你可以暂时理解为一个工具类,针对几个方法(这里就是middleware方法)比如middleware
可以提供一些工具方法)
public function attribute($key, $value)
{
if (! in_array($key, $this->allowedAttributes)) {
throw new InvalidArgumentException("Attribute [{$key}] does not exist.");
}
$this->attributes[Arr::get($this->aliases, $key, $key)] = $value;
return $this;
}
2.1 首先判断当前的方法名是否在数组 allowedAttributes
中存在。
allowedAttributes数组中存放的方法如下,就是限制了只有这几个方法可以进一步执行逻辑。否则抛出异常。
这几个方法也是group提供的几个参数方法。RouteRegistrar只提供下面几个方法的处理。
protected $allowedAttributes = [ 'as', 'domain', 'middleware', 'name', 'namespace', 'prefix', 'where', ];
2.2 Arr::get($this->aliases, $key, $key)
丛aliases数组中找到字段为$key
的值,如果找不到就用默认值 $key
替代。
再次强调:这里的key是middleware
, value是web
。
aliases是一个别名数组,通过别名寻找真实的方法名。把这个返回值作为id,第二个参数作为value存入attributes数组中。
简单说就是:
先寻找方法名的别名,如果得到了方法名的别名 就把他当做id,方法的参数作为value存入attributes数组。
当前的情况最后 应该得到的是这样的数据:$attribute = [ ... 'middleware' => 'web' ... ]
处理完attribute,然后返回RouteRegistrar对象
3.第三步基本和第二步一样,只是不再提供数组作为参数了,只能是一个字符串;代码如下:
return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
除了 middleware
以外的其他的方法。和第二步类同,调用RouteRegistrar
的attribute
方法。
唯一的区别就是因为middleware的参数可以是数组和字符串,而其他的方法只能是字符串。所以单独把middleware提出来单独处理。
这里总结下:
除非使用宏,提前动态绑定了middleware方法给router对象。
否则如果是middleware方法,就是把其方法名和参数保存到RouterRegister对象的attributes数组中。
其他方法也是如此。(这里是namespace方法雷同)
然后我们回到mapWebRoutes这个方法:
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
在保存了middleware和namespace方法的参数后。
最后执行了RouteRegistrar的group方法,这个group就是调用了router对象group方法,把前面得到的attributes属性当做参数传给router的group方法。Group方法在3.2.1中详细阐述了。不再累述
public function group($callback)
{
$this->router->group($this->attributes, $callback);
}
router的group方法会使用require引入
$callback
的路径或者触发$callback
方法基于$callback
是字符串还是闭包。不再详述。 (group方法逻辑参考前面group 3.2.1节。)
最后总结:
分析到这里我们可以发现。整个web.php文件,laravel把他当做了一整个group。
在laravel源码中,全局group也就是父group的 attributes有这么两个:
属性middleware
的值是web
;
*属性namespace
是$this->namespace
变量值; *
这个值的默认值是:protected $namespace = 'App\Http\Controllers';
到这里整个路由配置文件的加载已经很清楚了,(并不是单个路由实例的加载)
1.就是通过使用group方法加载配置文件,同时添加全局的属性attributes(middleware
namespace
);
2.group方法中合并了父group的attributes,如果有的话。 然后使用require引入路由代码。
3.然后加载。
后面我们就要分析单个路由实例的加载细节了。就是在加载配置文件中 我们所执行的 Route::get. Route::post等等的方法的逻辑了。
本作品采用《CC 协议》,转载必须注明作者和本文链接