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 以外的其他的方法。和第二步类同,调用RouteRegistrarattribute方法。

唯一的区别就是因为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 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!