Redirector 與 UrlGenerator 的關係

假設 routes 定義了以下路由:

Route::get('/', 'IndexController@welcome')->name('welcome');

我們可以使用下面的方法取得 URL:

route('welcome');

先大概了解這個實作方法:

function route($name, $parameters = [], $absolute = true)
{
return app('url')->route($name, $parameters, $absolute);
}

這個 url 曾在一開始分析 Routing 的時候提到過,它所對應的類別是 UrlGenerator,註冊方法如下:

$this->app->singleton('url', function ($app) {
$routes = $app['router']->getRoutes();

$app->instance('routes', $routes);

// UrlGenerator 建構子需要 RouteCollection 與 Request
$url = new UrlGenerator(
$routes, $app->rebinding(
'request', $this->requestRebinder()
)
);

// Session 解析器
$url->setSessionResolver(function () {
return $this->app['session'];
});

// APP_KEY 解析器
$url->setKeyResolver(function () {
return $this->app->make('config')->get('app.key');
});

// 當 RouteCollection 觸發 rebind 事件時,就重新設定給 UrlGenerator
$app->rebinding('routes', function ($app, $routes) {
$app['url']->setRoutes($routes);
});

return $url;
});

route() 函式,實際上是呼叫 UrlGenerator 的 route() 方法

public function route($name, $parameters = [], $absolute = true)
{
// 看看 RouteCollection 用名稱能不能找到
if (! is_null($route = $this->routes->getByName($name))) {
// 找到將會使用 RouteUrlGenerator 產生 url
return $this->toRoute($route, $parameters, $absolute);
}

// 找不到就讓它爆
throw new InvalidArgumentException("Route [{$name}] not defined.");
}

接著來看看 Redirector。Laravel 也有提供 helper 函式:

function redirect($to = null, $status = 302, $headers = [], $secure = null)
{
if (is_null($to)) {
return app('redirect');
}

return app('redirect')->to($to, $status, $headers, $secure);
}

這個函式提供兩種用法:

redirect(); // 取得 Redirector
redirect('/path'); // 取得 RedirectResponse

註冊方法如下:

$this->app->singleton('redirect', function ($app) {
$redirector = new Redirector($app['url']);

// 如果有 session 的話就設定
if (isset($app['session.store'])) {
$redirector->setSession($app['session.store']);
}

return $redirector;
});

Redirector 建構需要有 UrlGenerator。也因此兩個 helper 函式搭配後,下面的寫法都是通的:

redirect(route('welcome'));
redirect()->to(route('welcome'));
redirect()->route('welcome');
redirect()->action('IndexController@welcome');

這樣的設計是很有趣且可以學習的,Redirector 本身提供了基本的 to()away() 方法,可以導頁到指定的地方外,配合 UrlGenerator 與 Session 還可以做到 route()action()back() 等,與 Application 本身或狀態相關的導頁。

這是一個使用物件組合功能的好例子。