路由

基本路由

最基本的 Laravel 路由單純地接受一個 URI 和一個閉包,提供一個非常簡單且直覺的方式來定義路由:

Route::get('foo', function () {
    return 'Hello World';
});

預設路由檔案

routes 目錄中的路由檔案定義了所有的 Laravel 路由。這些檔案會自動被框架載入。routes/web.php 定義網頁介面的路由。這些路由被分配到 web 中介層群組,提供像是 session 狀態和 CSRF 保護的特性。routes/api.php 中的路由是無狀態的,且被分配到 api 中介層群組。

對大部分的應用程式來說,你會先由在 routes/web.php 檔案中定義路由開始。routes/web.php 中定義的路由可以透過在瀏覽器中輸入已定義的路由 URL 來訪問。例如,你可以在瀏覽器中瀏覽 http://your-app.dev/user 來訪問以下路由:

Route::get('/user', 'UsersController@index');

routes/api.php 檔案中定義的路由會透過 RouteServiceProvider 被巢狀在一個路由群組中。群組中的路由會自動被加上 /api 前綴,因此你不需要手動在檔案中的每個路由做設定。可以藉由修改 RouteServiceProvider 類別來調整前綴及其他路由群組選項。

可用的路由器方法

路由器能讓你註冊回應任何 HTTP 動詞的路由:

Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

有時候你可能需要註冊一個回應多種 HTTP 動詞的路由。你可以使用 match 方法做到。甚至可以透過 any 方法來註冊回應所有 HTTP 動詞的路由:

Route::match(['get', 'post'], '/', function () {
    //
});

Route::any('foo', function () {
    //
});

CSRF 保護

任何指向 web 路由檔案中定義的 POSTPUTDELETE 路由的 HTML 表單都應該包含一個 CSRF token 欄位。否則請求會被拒絕。詳細 CSRF 保護的說明請參閱 CSRF 文件

<form method="POST" action="/profile">
     {{ csrf_field() }} 
    ...
</form>

重導路由

如果要定義重導至另一個 URI 的路由,可以使用 Route::redirect 方法。此方法提供一個方便的快捷方式,所以你不需要定義ㄧ個完整的路由或控制器來執行一個單純的重導:

Route::redirect('/here', '/there', 301);

視圖路由

如果路由只需要回傳一個視圖,可以使用 Route::view 方法。如同 redirect 方法,此方法提供一個方便的快捷方式,因此不須要定義一個完整的路由或控制器。view 方法的第一個參數是 URI,第二個參數是視圖名稱。此外,也可以在可選的第三個參數傳入提供給視圖的資料陣列。

Route::view('/welcome', 'welcome');

Route::view('/welcome', 'welcome', ['name' => 'Taylor']);

路由參數

必要參數

有時候你可能需要從 URI 路由中取得一些字段。例如,你可能需要從 URL 取得使用者的 ID。你可以透過定義路由參數來取得:

Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

你可以依照路由需要,定義任何數量的路由參數:

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

路由的參數都會被放在 {} 大括號內,只由字母組成,且不包含 - 字元。使用 _ 來取代 -。路由參數根據它們的順序被注入到路由回呼或控制器中 - 回呼或控制器中的參數名稱不會有任何影響。

選擇性參數

有時候你可能需要指定路由參數,但是讓路由參數的存在是選擇性的。你可以藉由在參數名稱後面加上 ? 達成。記得給該路由的對應參數一個預設值:

Route::get('user/{name?}', function ($name = null) {
    return $name;
});

Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});

正規表示式限制

你可以在一個路由實例使用 where 方法來限制路由參數的格式。where 方法接受參數的名稱和限制參數格式的正規表示式:

Route::get('user/{name}', function ($name) {
    //
})->where('name', '[A-Za-z]+');

Route::get('user/{id}', function ($id) {
    //
})->where('id', '[0-9]+');

Route::get('user/{id}/{name}', function ($id, $name) {
    //
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

全域限制

如果你想要一個路由參數總是透過指定的正規表示式限制,可以使用 pattern 方法。在 RouteServiceProvider 中的 boot 方法來定義這些格式:

/**
 * 定義你的模型綁定、格式過濾器等。
 *
 * @return void
 */
public function boot()
{
    Route::pattern('id', '[0-9]+');

    parent::boot();
}

一旦定義好格式後,它將自動應用於使用該參數名稱的所有路由:

Route::get('user/{id}', function ($id) {
    // {id} 為數字時才會執行...
});

命名路由

命名路由讓你更方便為特定路由產生 URL 或重導。可以藉由鏈結 name 方法來為路由定義加上名稱:

Route::get('user/profile', function () {
    //
})->name('profile');

也可以為控制器行為指定名稱:

Route::get('user/profile', 'UserController@showProfile')->name('profile');

產生命名路由的 URL

指定名稱給路由後,便可以在使用全域的 route 方法產生 URL 或重導時使用路由名稱。

// 產生 URL...
$url = route('profile');

// 產生重導...
return redirect()->route('profile');

如果命名路由有定義參數,可以把參數傳給 route 方法的第二個參數。提供的參數會自動被插入到 URL 中正確的位置:

Route::get('user/{id}/profile', function ($id) {
    //
})->name('profile');

$url = route('profile', ['id' => 1]);

檢查目前路由

如果你想要確定目前的請求是否有被路由到指定的命名路由,可以使用 Route 實例的 named 方法。例如,可以在路由中介層中檢查目前的路由名稱:

/**
 * 處理傳入的請求。
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Closure  $next
 * @return mixed
 */
public function handle($request, Closure $next)
{
    if ($request->route()->named('profile')) {
        //
    }

    return $next($request);
}

路由群組

路由群組允許你共用路由屬性,像是中介層、命名空間,你可以利用路由群組套用這些屬性到多個路由,而不需在每個路由都設定一次。用陣列來指定共用屬性,當作 Route::group 方法的第一個參數:

中介層

要指定中介層到所有群組內的路由,你可以在定義群組前使用 middleware 方法。中介層將會依照在陣列中的順序執行:

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // 使用 first 和 second 中介層
    });

    Route::get('user/profile', function () {
        // 使用 first 和 second 中介層
    });
});

命名空間

另一個常見的用法是利用 namespace 方法來指定相同的 PHP 命名空間中給一群控制器。

Route::namespace('Admin')->group(function () {
    // 「App\Http\Controllers\Admin」 命名空間下的控制器
});

記得,預設 RouteServiceProvider 會在命名空間群組內導入路由檔案,讓你不用指定完整的 App\Http\Controllers 命名空間前綴就能註冊控制器路由。所以,我們只需要指定在基底 App\Http\Controllers 命名空間之後的命名。

子網域路由

路由群組也可以用來處理子網域路由。子網域可能會像是路由 URI 指定的路由參數,可以在路由或控制器中取得一部份的子網域來使用。在定義群組前呼叫 domain 方法來指定子網域:

Route::domain('{account}.myapp.com')->group(function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

路由前綴

prefix 方法可以用來為群組中的每個路由加上 URI 前綴。例如,你可能想要在群組中所有的路由 URI 加上 admin 前綴:

Route::prefix('admin')->group(function () {
    Route::get('users', function () {
        // 符合 「/admin/users」 URL
    });
});

路由模型綁定

當注入模型的 ID 到路由或控制器行為時,你將會經常查詢來取得與該 ID 對應的模型。Laravel 路由模型綁定提供方便的方式來自動注入類別實例至路由中。例如,除了注入一個使用者的 ID,你可以注入與給定 ID 相符的完整 User 模型實例。

隱式綁定

Laravel 會自動解析路由或控制器行為中變數名稱與路由片段名稱相符,並由型別提示所定義的 Eloquent 模型。例如:

Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});

自定鍵名

由於 $user 變數的型別提示為 App\User Eloquent 模型且與 URI 的 {user} 片段相符,Laravel 會自動注入與請求 URI 的 ID 值對應的模型實例。如果資料庫中找不到符合的模型實例,會自動產生一個 404 HTTP 回應。

如果你想讓模型綁定取得模型類別時使用 id 以外的資料庫欄位,你可以覆寫 Eloquent 模型的 getRouteKeyName 方法:

/**
 * 取得路由的模型鍵值。
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}

顯式綁定

要註冊一個顯式綁定,可使用路由器的 model 方法為給定參數指定類別。你必須在 RouteServiceProviderboot 方法中定義顯式模型綁定:

public function boot()
{
    parent::boot();

    Route::model('user', App\User::class);
}

接著,定義包含 {user} 參數的路由:

Route::get('profile/{user}', function (App\User $user) {
    //
});

因為我們已經綁定所有的 {user} 參數至 App\User 模型,User 實例會被注入至該路由。例如,一個至 profile/1 的請求會注入 ID 為 1 的 User 實例。

如果資料庫中找不到符合的模型實例,會自動產生一個 404 HTTP 回應。

自訂解析邏輯

如果你希望使用你自己的解析邏輯,可以使用 Route::bind 方法。你傳遞至 bind 方法的閉包會取得 URI 的部分值,且應該回傳你想注入至路由的類別實例:

public function boot()
{
    parent::boot();

    Route::bind('user', function ($value) {
        return App\User::where('name', $value)->first() ?? abort(404);
    });
}

表單欺騙方法

HTML 表單沒有支援 PUTPATCHDELETE 動作。所以在定義由 HTML 表單所呼叫的 PUTPATCHDELETE 路由時,你會需要在表單中增加隱藏的 _method 欄位。隨著 _method 欄位送出的值將被視為 HTTP 請求方法使用:

<form action="/foo/bar" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="_token" value=" {{ csrf_token() }} ">
</form>

也可以用 method_field 輔助函式來產生 _method 輸入欄位:

 {{ method_field('PUT') }} 

訪問目前路由

你可以用 Route facade 的 currentcurrentRouteNamecurrentRouteAction 方法來取得有關處理傳入請求的路由的資訊:

$route = Route::current();

$name = Route::currentRouteName();

$action = Route::currentRouteAction();

請參考 Route facade 的基礎類別路由實例這兩份 API 文件來詳閱可用的方法。