Laravel 深刻理解路由和URL生成

在模板中咱們通常不會直接寫死url,而是用url助手生成url,本文介紹一下url助手的使用以及遇到的一些比較頭疼的問題。php

首先,咱們建立了一個路由:

Route::get('articles',['uses'=>'ArticlesController@index','as'=>'articles.index']);html

假設咱們的項目部署在域名根目錄,那麼能夠經過下面的url訪問:

http://localhost/articleslaravel

如今,咱們在模板中生成連接,有幾種方式:
  1. 簡單模式數組

    ```<a href="{{ url('/articles') }}">連接</a>

// or <a href="{{ URL::to('/articles') }}">連接</a> //爲了方便閱讀,下面省略html標籤```post

這種方式,只是簡單的將你指定的路徑拼接到網站根url上。
  1. 路由模式測試

    ```URL::route(&#39;articles.index&#39;)```
    
        這種方式是指定匹配註冊路由時的 &#39;as&#39; 參數,獲得註冊的uri。
  2. 控制器動做模式網站

    ```URL::action(&#39;ArticlesController@index&#39;)```
    
        這種方式是根據註冊路由時 &#39;uses&#39; 參數,自動生成映射到控制器方法的uri,規則同 `Route::controller()` 。

舉例以下:ui

```ArticlesController@index =&gt; articles

ArticlesController@getAdd => articles/add ArticlesController@postAdd => articles/add ArticlesController@getDelete => articles/delete```url

**基本教程到此結束,接下來咱們來面對一些使人惱怒的狀況。**

如今,路由變得更加複雜了,咱們定義了一個這樣的:

Route::controller(&#39;users&#39;,&#39;UsersController&#39;);code

一條簡單的語句,Laravel會自動反射 `UsersController` 類,並掃描其中的方法,而後生成普通的映射,舉例說明吧,假設控制器中有如下方法:
function getEdit();
function postEdit();```

    實際上,經過 `Route::controller()` 最終結果**相似**於:

```Route::get(&#39;users&#39;,[&#39;uses&#39;=&gt;&#39;UsersController@getIndex&#39;]);
Route::get(&#39;users/edit&#39;,[&#39;uses&#39;=&gt;&#39;UsersController@getEdit&#39;]);
Route::post(&#39;users/edit&#39;,[&#39;uses&#39;=&gt;&#39;UsersController@postEdit&#39;]);```

    說白了,高級路由方法只是對基本方法的封裝。

    好的,如今咱們來生成一條url:

```echo URL::action(&#39;UsersController@getEdit&#39;);```

    如願以償獲得 `http://localhost/users/edit` ,可是咱們要加點querystring參數呢?也就是俗稱的 get參數。

    咱們想要獲得 `http://localhost/users/edit?id=1` 應該怎麼生成?

    聰明人已經注意到了 `URL::action()` 有第二個參數,能夠指定一個數組,做爲url參數,那好,咱們來試試。

```echo URL::action(&#39;UsersController@getEdit&#39;,[&#39;id&#39;=&gt;1]);```

    好了?NO!!你獲得的將是:

```http://localhost/users/edit/1```

    若是你再加一個參數:

```echo URL::action(&#39;UsersController@getEdit&#39;,[&#39;id&#39;=&gt;1,&#39;author&#39;=&gt;&#39;admin&#39;]);```

    獲得的是:

```http://localhost/users/edit/1/admin```

    它根本就忽視了你指定的key,而且沒有做爲 `?id=1&amp;author=admin` 附加在url末尾。

    **爲何?!!** &nbsp;

    確定是哪裏出問題了,這裏你不用去扒源碼,緣由我會在下面給出。

    咱們先用另一種方式來測試,前面說過,高級路由是對基本路由的封裝,那麼咱們就構建一個基本路由來測試這個問題!

```Route::get(&#39;test/edit&#39;,[&#39;uses&#39;=&gt;&#39;TestController@getEdit&#39;])```

    生成URL:

```echo URL::action(&#39;TestController@getEdit&#39;,[&#39;id&#39;=&gt;1]);```

    獲得:

```http://localhost/test/edit?id=1```

    這特麼是對的啊?不是說 Route::controller() 是對 Route::get() 這種基本方法的封裝麼?

    沒錯,但咱們憑什麼就認爲 `Route::controller(&#39;users&#39;,&#39;UsersController&#39;);` 徹底等於 `Route::get(&#39;users/edit&#39;,[&#39;uses&#39;=&gt;&#39;UsersController@getEdit&#39;]);` 等等?至少 uri部分不同,我一開始也只是用了」相似「來講明。

    經過對源碼的剖析,我發現了,當經過 `Route::controller()` 註冊路由的時候,它實際上會在轉化的時候加點料進去,請查看 `\Illuminate\Routing\ControllerInspector::addUriWildcards` 方法。

    實際上,會轉化爲:

```Route::get(&#39;users/edit/{one?}/{two?}/{three?}/{four?}/{five?}&#39;,[&#39;uses&#39;=&gt;&#39;UsersController@getEdit&#39;])```

    Laravel默認url生成有個缺陷,它是按照這樣的步驟去匹配參數的。

```echo URL::action(&#39;TestController@getEdit&#39;,[&#39;id&#39;=&gt;1]);```

    它會遍歷$parameters,也就是 `[&#39;id&#39;=&gt;1]`。

1.  它會檢測路由註冊uri中有沒有 `{id?}` 這個字符串,有的話就把值放在這個位置,好比 `users/edit/{id?}` 這樣的話,就會造成 `users/edit/1`。

2.  將參數數組中剩下的元素再次遍歷,按順序替換到uri中剩下的 `{.*\?}` 這種格式的字符串。

3.  最後將剩餘的 `{.*\?}` 刪掉,若是$parameters還有剩餘元素,則會被做爲querystring附加到url末尾。

    也就是說,除非咱們在 `id` 前面增長五個參數,不然 `id` 的值只能出如今uri路徑中。

    很奇葩不是麼?

    那麼如何解決這個問題?

1.  querystring手寫到url後面,能夠經過 `http_build_query`

            ```$params = [&#39;id&#39;=&gt;1];
echo URL::action(&#39;TestController@getEdit&#39;).&#39;?&#39;.http_build_query($params);```
2.  修改laravel源碼,在 `vendor/laravel/framework/src/Illuminate/Support/helpers.php` 文件中,找到 `preg_replace_sub` ,做以下修改:

            ```function preg_replace_sub($pattern, &amp;$replacements, $subject)
{
    return preg_replace_callback($pattern, function($match) use (&amp;$replacements)
    {
        foreach ($replacements as $key =&gt; $value)
        {
            //return array_shift($replacements);
            if (is_numeric($key)) {
                unset($replacements[$key]);
                return $value;
            }
        }

        }, $subject);
}```

    用哪一種,取決於你本身。

    就說這麼多吧,有什麼疑問請發郵件給我,郵箱地址在頁面底部。
相關文章
相關標籤/搜索