原文地址: Laravel 深入理解路由和URL生成
在模板中我們一般不會直接寫死url兔港,而是用url助手生成url托酸,本文介紹一下url助手的使用以及遇到的一些比較頭疼的問題跌穗。
首先走触,我們創(chuàng)建了一個路由:
Route::get('articles',['uses'=>'ArticlesController@index','as'=>'articles.index']);
假設(shè)我們的項(xiàng)目部署在域名根目錄澎剥,那么可以通過下面的url訪問:
http://localhost/articles
現(xiàn)在拘哨,我們在模板中生成鏈接谋梭,有幾種方式:
- 簡單模式
<a href="{{ url('/articles') }}">鏈接</a>
// or
<a href="{{ URL::to('/articles') }}">鏈接</a>
//為了方便閱讀,下面省略html標(biāo)簽
這種方式倦青,只是簡單的將你指定的路徑拼接到網(wǎng)站根url上瓮床。
- 路由模式
URL::route('articles.index')
這種方式是指定匹配注冊路由時的 'as' 參數(shù),得到注冊的uri产镐。
- 控制器動作模式
URL::action('ArticlesController@index')
這種方式是根據(jù)注冊路由時 'uses' 參數(shù)隘庄,自動生成映射到控制器方法的uri,規(guī)則同 Route::controller()
磷账。 舉例如下:
ArticlesController@index => articles
ArticlesController@getAdd => articles/add
ArticlesController@postAdd => articles/add
ArticlesController@getDelete => articles/delete
基本教程到此結(jié)束峭沦,接下來我們來面對一些令人惱怒的情況。
現(xiàn)在逃糟,路由變得更加復(fù)雜了吼鱼,我們定義了一個這樣的:
Route::controller('users','UsersController');
一條簡單的語句,Laravel會自動反射 UsersController
類绰咽,并掃描其中的方法菇肃,然后生成普通的映射,舉例說明吧取募,假設(shè)控制器中有以下方法:
function getIndex();
function getEdit();
function postEdit();
實(shí)際上琐谤,通過 Route::controller()
最終結(jié)果類似于:
Route::get('users',['uses'=>'UsersController@getIndex']);
Route::get('users/edit',['uses'=>'UsersController@getEdit']);
Route::post('users/edit',['uses'=>'UsersController@postEdit']);
說白了,高級路由方法只是對基本方法的封裝玩敏。
好的斗忌,現(xiàn)在我們來生成一條url:
echo URL::action('UsersController@getEdit');
如愿以償?shù)玫?http://localhost/users/edit
质礼,但是我們要加點(diǎn)querystring參數(shù)呢?也就是俗稱的 get參數(shù)织阳。
我們想要得到 http://localhost/users/edit?id=1
應(yīng)該怎么生成眶蕉?
聰明人已經(jīng)注意到了 URL::action()
有第二個參數(shù),可以指定一個數(shù)組唧躲,作為url參數(shù)造挽,那好,我們來試試弄痹。
echo URL::action('UsersController@getEdit',['id'=>1]);
好了饭入?NO!!你得到的將是:
http://localhost/users/edit/1
如果你再加一個參數(shù):
echo URL::action('UsersController@getEdit',['id'=>1,'author'=>'admin']);
得到的是:
http://localhost/users/edit/1/admin
它根本就忽視了你指定的key,并且沒有作為 ?id=1&author=admin
附加在url末尾肛真。
為什么谐丢?!毁欣!
肯定是哪里出問題了庇谆,這里你不用去扒源碼,原因我會在下面給出凭疮。
我們先用另外一種方式來測試饭耳,前面說過,高級路由是對基本路由的封裝执解,那么我們就構(gòu)建一個基本路由來測試這個問題寞肖!
Route::get('test/edit',['uses'=>'TestController@getEdit'])
生成URL:
echo URL::action('TestController@getEdit',['id'=>1]);
得到:
http://localhost/test/edit?id=1
這特么是對的啊衰腌?不是說 Route::controller() 是對 Route::get() 這種基本方法的封裝么新蟆?
沒錯,但我們憑什么就認(rèn)為 Route::controller('users','UsersController');
完全等于 Route::get('users/edit',['uses'=>'UsersController@getEdit']);
等等右蕊?至少 uri部分不一樣,我一開始也只是用了”類似“來說明琼稻。
通過對源碼的剖析,我發(fā)現(xiàn)了饶囚,當(dāng)通過 Route::controller()
注冊路由的時候帕翻,它實(shí)際上會在轉(zhuǎn)化的時候加點(diǎn)料進(jìn)去,請查看 \Illuminate\Routing\ControllerInspector::addUriWildcards
方法萝风。
實(shí)際上嘀掸,會轉(zhuǎn)化為:
Route::get('users/edit/{one?}/{two?}/{three?}/{four?}/{five?}',['uses'=>'UsersController@getEdit'])
Laravel默認(rèn)url生成有個缺陷,它是按照這樣的步驟去匹配參數(shù)的规惰。
echo URL::action('TestController@getEdit',['id'=>1]);
它會遍歷$parameters,也就是 ['id'=>1]
睬塌。
- 它會檢測路由注冊uri中有沒有
{id?}
這個字符串,有的話就把值放在這個位置,比如users/edit/{id?}
這樣的話揩晴,就會形成users/edit/1
勋陪。 - 將參數(shù)數(shù)組中剩下的元素再次遍歷,按順序替換到uri中剩下的
{.*\?}
這種格式的字符串文狱。 - 最后將剩余的
{.*\?}
刪掉粥鞋,如果$parameters還有剩余元素,則會被作為querystring附加到url末尾瞄崇。
也就是說,除非我們在 id
前面增加五個參數(shù)壕曼,否則 id
的值只能出現(xiàn)在uri路徑中苏研。
很奇葩不是么?
那么如何解決這個問題腮郊?
- querystring手寫到url后面摹蘑,可以通過
http_build_query
$params = ['id'=>1];
echo URL::action('TestController@getEdit').'?'.http_build_query($params);
- 修改laravel源碼,在
vendor/laravel/framework/src/Illuminate/Support/helpers.php
文件中轧飞,找到preg_replace_sub
衅鹿,作如下修改:
function preg_replace_sub($pattern, &$replacements, $subject)
{
return preg_replace_callback($pattern, function($match) use (&$replacements)
{
foreach ($replacements as $key => $value)
{
//return array_shift($replacements);
if (is_numeric($key)) {
unset($replacements[$key]);
return $value;
}
}
}, $subject);
}
用哪種,取決于你自己过咬。