3.2.3 - Laravel - 5.6 - Route - 路由配置檔案載入mapWebRoutes方法解析

HarveyNorman發表於2020-09-26

在路由載入配置檔案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方法來處理。

__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類觸發的方法如果不在類中出現,那就去巨集集合中檢視是否提前設定了這個方法,然後觸發。

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的引數作為value存入RouteRegistrar物件的attributes陣列中

is_array($parameters[0]) ? $parameters[0] : $parameters針對middleware方法可以傳入陣列引數,也可以傳入字串作為引數。

可以看下attribute方法。最後返回的是RouteRegistrar物件。

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提供的幾個引數方法。

protected $allowedAttributes = [
'as', 'domain', 'middleware', 'name', 'namespace', 'prefix', 'where',
];

2.2 Arr::get($this->aliases, $key, $key) 叢aliases陣列中找到欄位為$key的值,如果找不到就用預設值 $key替代。aliases是一個別名陣列,通過別名尋找真實的方法名。
把這個返回值作為id,第二個引數作為value存入attributes陣列中。

簡單說就是:
先尋找方法名的別名,然後得到了方法名的返回值作為id,方法的引數作為value存入attributes陣列。


3.第三步

return (new RouteRegistrar($this))->attribute($method, $parameters[0]);

針對其他的方法。和第二步類同,呼叫RouteRegistrarattribute方法。唯一的區別就是因為middleware的引數可以是陣列和字串,其他的方法只是字串。所以單獨把middleware提出來單獨處理。

總結下:
除非提前動態繫結了middleware方法給router物件。
否則如果是middleware方法,就是把其方法名和引數儲存到RouterRegister物件的attributes陣列中。
其他方法也是如此。

然後我們回到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方法。

public function group($callback)
    {
        $this->router->group($this->attributes, $callback);
    }

router的group方法使用require引入$callback的路徑或者觸發$callback方法基於$callback是字串還是閉包。不再詳述。 (group方法邏輯參考前面group方法分析章節。)

總結:
分析到這裡我們可以發現。整個web.php檔案,laravel把他當做了一整個group。在我們的例子中,這個配置檔案的middleware是web。namespace是$this->namespace變數值, 預設值是:
protected $namespace = 'App\Http\Controllers';

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章