網址生成

簡介

Laravel 提供了幾個幫助函式來為應用生成 URL。主要用於在模板和 API 回應中生成 URL 或者在應用的其它部分生成轉址回應

基礎

生成 URLs

幫助函式 url() 可以用於應用的任何一個 URL。生成的 URL 將自動使用當前請求中的協定 (HTTP or HTTPS) 和網域:

$post = App\Models\Post::find(1);

echo url("/posts/{$post->id}");

// http://example.com/posts/1

取得當前 URL

如果沒有為幫助函式 url() 提供路徑,則會回傳一個 Illuminate\Routing\UrlGenerator 實例,來允許你取得有關當前 URL 的資料:

// Get the current URL without the query string...
echo url()->current();

// Get the current URL including the query string...
echo url()->full();

// Get the full URL for the previous request...
echo url()->previous();

上面的這些方法都可以通過 URL Facade 來訪問:

use Illuminate\Support\Facades\URL;

echo URL::current();

命名路由的 URLs

幫助函式 route() 可以用於為指定路由生成 URL。命名路由生成的 URL 不與路由上定義的 URL 相耦合。因此就算路由的 URL 有任何改變,都不需要對 route() 呼叫進行任何更改。例如假設你的應用包含以下路由:

Route::get('/post/{post}', function () {
    //
})->name('post.show');

要生成此路由的 URL ,可以像這樣使用幫助函式 route() :

echo route('post.show', ['post' => 1]);

// http://example.com/post/1

當然, route() 幫助函式也能傳入多個參數來生成 URLs

Route::get('/post/{post}/comment/{comment}', function () {
    //
})->name('comment.show');

echo route('comment.show', ['post' => 1, 'comment' => 3]);

// http://example.com/post/1/comment/3

任何陣列的元素的鍵值如有不符合路由參數的部分將自動轉為 URL 的查詢字串

echo route('post.show', ['post' => 1, 'search' => 'rocket']);

// http://example.com/post/1?search=rocket

Eloquent 模型

你將很常利用 Eloquent 模型的主鍵來生成 URLs。因此,你何不直接傳入 Eloquent 模型作為參數值, route() 將會自動的取用模型的主鍵

echo route('post.show', ['post' => $post]);

簽名 URLs

Laravel 允許你輕鬆地為命名路徑創建「簽名」URL,這些 URL 在查詢字串後附加了「簽名」Hash,允許 Laravel 驗證 URL 自創建以來未被修改過。簽名 URL 對於可公開訪問但需要一層防止 URL 操作的路由特別有用。

例如,你可以使用簽名 URL 來實現通過電子郵件發送給客戶的公開「取消訂閱」連結。要創建命名路由的簽名 URL ,請使用 URL Facade 的 signedRoute()

use Illuminate\Support\Facades\URL;

return URL::signedRoute('unsubscribe', ['user' => 1]);

如果要生成具有有效期的臨時簽名路由 URL,可以使用 temporarySignedRoute() 。當 Laravel 驗證一個臨時簽名路由 URL,它將確保被加入在簽名 URL 的過期時間戳記還沒有被改動

use Illuminate\Support\Facades\URL;

return URL::temporarySignedRoute(
    'unsubscribe', now()->addMinutes(30), ['user' => 1]
);

驗證簽名路由請求

要驗證傳入請求是否具有有效簽名,你應該對傳入的 Request 呼叫 hasValidSignature()

use Illuminate\Http\Request;

Route::get('/unsubscribe/{user}', function (Request $request) {
    if (! $request->hasValidSignature()) {
        abort(401);
    }

    // ...
})->name('unsubscribe');

或者,你可以將 Illuminate\Routing\Middleware\ValidateSignature 中介層分配給路由。如果它不存在,則應該在 HTTP 內核的 routeMiddleware 陣列中為此中介層分配一個鍵

/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
];

在內核中註冊中介層後,你可以將其附加到路由中。如果傳入請求沒有有效簽名,則中介層將自動返回 403 錯誤回應

Route::post('/unsubscribe/{user}', function (Request $request) {
    // ...
})->name('unsubscribe')->middleware('signed');

控制器行為的 URLs

action() 可以為給定的控制器行為生成 URL

use App\Http\Controllers\HomeController;

$url = action([HomeController::class, 'index']);

如果控制器方法接收路由參數,你可以通過第二個參數傳遞

$url = action([UserController::class, 'profile'], ['id' => 1]);

預設值

對於某些應用,你可能希望為某些 URL 參數的請求範圍指定預設值。例如假設多數的路由定義了 {locale} 參數

Route::get('/{locale}/posts', function () {
    //
})->name('post.index');

每次呼叫幫助函數 route() 都要傳入 locale 也是一件很麻煩的事情。 因此使用 URL::defaults() 定義這個參數的預設值,可以讓該參數始終存在當前請求中。然後就能從路由中介層呼叫此方法來訪問當前請求

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\URL;

class SetDefaultLocaleForUrls
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return \Illuminate\Http\Response
     */
    public function handle($request, Closure $next)
    {
        URL::defaults(['locale' => $request->user()->locale]);

        return $next($request);
    }
}

一旦設置了 locale 參數的預設值,你就不再需要通過幫助函式 route() 生成 URL 時傳遞它的值

URL 預設 & 中介層 優先

設定 URL 預設值會干擾 Laravel 處理隱式 Model Binding。因此你應該把你的中介層設到比 Laravel 的 SubstituteBindings 中介層優先。你能夠透過應用 HTTP 內核的 $middlewarePriority 屬性來達成,確保你的中介層擺在 SubstituteBindings 之前

再說明一次, $middlewarePriority 屬性被定義在 Illuminate\Foundation\Http\Kernerl 類別。 你能夠複製它的設定並貼入應用的 HTTP 內核檔案以修改它

/**
 * The priority-sorted list of middleware.
 *
 * This forces non-global middleware to always be in the given order.
 *
 * @var array
 */
protected $middlewarePriority = [
    // ...
     \App\Http\Middleware\SetDefaultLocaleForUrls::class,
     \Illuminate\Routing\Middleware\SubstituteBindings::class,
     // ...
];
Select a repo