> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# URL 生成

> 說明 Laravel 的 url() helper、具名路由 URL、簽章 URL、controller action URL 的產生方法。

## 前言

Laravel 提供多個產生應用 URL 的 helper。
在樣板或 API 回應中建構連結、或產生導向應用其他位置的 redirect 回應時特別方便。

## 基本用法

### 產生 URL

用 `url` helper 產生任意 URL。
產生的 URL 會自動使用當前處理中請求的 scheme（HTTP 或 HTTPS）與 host。

```php theme={null}
$post = App\Models\Post::find(1);

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

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

要包含 query string 參數的 URL，使用 `query` 方法。

```php theme={null}
echo url()->query('/posts', ['search' => 'Laravel']);

// https://example.com/posts?search=Laravel

echo url()->query('/posts?sort=latest', ['search' => 'Laravel']);

// http://example.com/posts?sort=latest&search=Laravel
```

若指定路徑中已存在的 query 參數，既有值會被覆寫。

```php theme={null}
echo url()->query('/posts?sort=latest', ['sort' => 'oldest']);

// http://example.com/posts?sort=oldest
```

也可以把陣列值當作 query 參數傳入。這些值會被適當地標鍵並編碼到產生的 URL 中。

```php theme={null}
echo $url = url()->query('/posts', ['columns' => ['title', 'body']]);

// http://example.com/posts?columns%5B0%5D=title&columns%5B1%5D=body

echo urldecode($url);

// http://example.com/posts?columns[0]=title&columns[1]=body
```

### 取得目前 URL

若不對 `url` helper 指定路徑，會回傳 `Illuminate\Routing\UrlGenerator` 實例，可存取目前 URL 相關資訊。

```php theme={null}
// 不含 query string 的目前 URL
echo url()->current();

// 含 query string 的目前 URL
echo url()->full();
```

這些方法也可透過 `URL` [facade](./facades) 存取。

```php theme={null}
use Illuminate\Support\Facades\URL;

echo URL::current();
```

### 取得前一個 URL

有時想知道使用者剛才所在的 URL。可用 `url` helper 的 `previous` 或 `previousPath` 方法取得。

```php theme={null}
// 前一個請求的完整 URL
echo url()->previous();

// 前一個請求的路徑
echo url()->previousPath();
```

也可以透過 session 取得前一個 URL。

```php theme={null}
use Illuminate\Http\Request;

Route::post('/users', function (Request $request) {
    $previousUri = $request->session()->previousUri();

    // ...
});
```

也能經 session 取得前一個訪問 URL 的路由名稱。

```php theme={null}
$previousRoute = $request->session()->previousRoute();
```

## 具名路由的 URL

`route` helper 用來產生 [具名路由](./routing#具名路由) 的 URL。
使用具名路由可不依賴實際 URL 產生 URL。
因此路由的 URL 變更時，`route` 呼叫不需修改。

```php theme={null}
Route::get('/post/{post}', function (Post $post) {
    // ...
})->name('post.show');
```

產生此路由的 URL 如下：

```php theme={null}
echo route('post.show', ['post' => 1]);

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

也支援有多個參數的路由。

```php theme={null}
Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) {
    // ...
})->name('comment.show');

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

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

未對應路由定義參數的多餘陣列元素會加到 URL 的 query string。

```php theme={null}
echo route('post.show', ['post' => 1, 'search' => 'rocket']);

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

### Eloquent model

常會用 Eloquent model 的路由 key（通常是主鍵）產生 URL。
可將 Eloquent model 作為參數值傳入。`route` helper 會自動取出 model 的路由 key。

```php theme={null}
echo route('post.show', ['post' => $post]);
```

### 簽章 URL

Laravel 可簡單為具名路由建立「簽章」URL。
這些 URL 會在 query string 附加「簽章」雜湊，Laravel 可驗證 URL 建立後未被竄改。
簽章 URL 特別適用於需公開存取但需防止 URL 操作的路由。

例如可用簽章 URL 為寄給客戶的 email 實作公開「取消訂閱」連結。
用 `URL` facade 的 `signedRoute` 方法建立簽章 URL。

```php theme={null}
use Illuminate\Support\Facades\URL;

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

在 `signedRoute` 指定 `absolute` 引數，可將簽章 URL 雜湊排除 domain。

```php theme={null}
return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);
```

要產生指定時間後失效的暫時簽章 URL，使用 `temporarySignedRoute`。
Laravel 在驗證暫時簽章 URL 時，會確認編碼於簽章 URL 中的到期時間戳未過期。

```php theme={null}
use Illuminate\Support\Facades\URL;

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

#### 簽章 URL 的驗證流程

```mermaid theme={null}
sequenceDiagram
    participant App as Laravel 應用
    participant User as 使用者
    participant Mail as 郵件
    App->>App: URL::signedRoute() / temporarySignedRoute()
    App->>Mail: 寄送簽章 URL 郵件
    Mail->>User: 收到郵件
    User->>App: 點擊 URL（GET 請求）
    App->>App: 驗證簽章<br>hasValidSignature()
    alt 簽章有效
        App->>User: 執行處理（如取消訂閱）
    else 簽章無效／過期
        App->>User: 403 錯誤
    end
```

#### 簽章路由請求的驗證

要確認收到的請求含有有效簽章，可對 `Illuminate\Http\Request` 實例呼叫 `hasValidSignature`。

```php theme={null}
use Illuminate\Http\Request;

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

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

要在驗證時忽略特定 query 參數，使用 `hasValidSignatureWhileIgnoring`。

```php theme={null}
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
    abort(401);
}
```

除了使用收到的請求實例外，也可將 `signed`（`Illuminate\Routing\Middleware\ValidateSignature`）[middleware](./middleware) 指派到路由。
若收到的請求無有效簽章，middleware 會自動回傳 `403` HTTP 回應。

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

若簽章 URL 不含 domain，可對 middleware 指定 `relative` 引數。

```php theme={null}
Route::post('/unsubscribe/{user}', function (Request $request) {
    // ...
})->name('unsubscribe')->middleware('signed:relative');
```

#### 對無效簽章路由的回應

存取已過期的簽章 URL 時，會顯示 `403` HTTP 狀態碼的通用錯誤頁。
可在應用的 `bootstrap/app.php` 定義 `InvalidSignatureException` 例外的自訂「render」closure 來自訂此行為。

```php theme={null}
use Illuminate\Routing\Exceptions\InvalidSignatureException;

->withExceptions(function (Exceptions $exceptions): void {
    $exceptions->render(function (InvalidSignatureException $e) {
        return response()->view('errors.link-expired', status: 403);
    });
})
```

## Controller Action 的 URL

`action` 函式產生指定 controller action 的 URL。

```php theme={null}
use App\Http\Controllers\HomeController;

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

若 controller 方法接受路由參數，在函式第 2 個引數傳入路由參數的關聯陣列。

```php theme={null}
$url = action([UserController::class, 'profile'], ['id' => 1]);
```

## Fluent URI 物件

Laravel 的 URI 類別提供便利的 fluent 介面來透過物件建立與操作 URI。

```php theme={null}
use App\Http\Controllers\UserController;
use Illuminate\Support\Uri;

// 從字串產生 URI 實例
$uri = Uri::of('https://example.com/path');

// 產生路徑、具名路由、controller action 的 URI
$uri = Uri::to('/dashboard');
$uri = Uri::route('users.show', ['user' => 1]);
$uri = Uri::signedRoute('users.show', ['user' => 1]);
$uri = Uri::temporarySignedRoute('user.index', now()->plus(minutes: 5));
$uri = Uri::action([UserController::class, 'index']);

// 從當前請求 URL 產生 URI 實例
$uri = $request->uri();
```

取得 URI 實例後可 fluent 地變更。

```php theme={null}
$uri = Uri::of('https://example.com')
    ->withScheme('http')
    ->withHost('test.com')
    ->withPort(8000)
    ->withPath('/users')
    ->withQuery(['page' => 2])
    ->withFragment('section-1');
```

## 預設 URL 參數

有時要為特定 URL 參數指定整個請求範圍的預設值。
例如多條路由都有定義 `{locale}` 參數。

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

每次呼叫 `route` helper 都要傳 `locale` 很麻煩。
此時可用 `URL::defaults` 方法定義此參數在當前請求中常用的預設值。
在[路由 middleware](/zh-TW/middleware#對路由套用-middleware) 中呼叫此方法就能存取當前請求。

```php theme={null}
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\Response;

class SetDefaultLocaleForUrls
{
    /**
     * 處理收到的請求。
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        URL::defaults(['locale' => $request->user()->locale]);

        return $next($request);
    }
}
```

設定 `locale` 參數的預設值後，用 `route` helper 產生 URL 時不需再傳入該值。

<Info>
  URL 預設值設定可能會妨礙 Laravel 的隱式 model binding 處理。
  請以[middleware 優先順序設定](./middleware)讓設定 URL 預設值的 middleware 在 Laravel 本身的 `SubstituteBindings` middleware 之前執行。
  可在應用的 `bootstrap/app.php` 使用 `priority` middleware 方法設定。

  ```php theme={null}
  ->withMiddleware(function (Middleware $middleware): void {
      $middleware->prependToPriorityList(
          before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
          prepend: \App\Http\Middleware\SetDefaultLocaleForUrls::class,
      );
  })
  ```
</Info>


## Related topics

- [Laravel AI SDK](/zh-TW/ai-sdk.md)
- [Laravel Folio](/zh-TW/folio.md)
- [Laravel Boost](/zh-TW/boost.md)
- [引擎 API 整合應用開發指南 - VOICEVOX for Laravel](/zh-TW/packages/laravel-voicevox/app-guide.md)
- [Laravel 13 新功能彙總](/zh-TW/blog/laravel-13-new-features.md)
