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

# 套件的路由註冊與快取

> 從 Laravel 13 的實作解說 loadRoutesFrom 的角色、中介軟體與名稱的分離，以及將設定變更反映至路由快取的步驟。

當套件提供 HTTP 端點時，路由不僅要在開發環境中能運作，在使用端應用程式建立路由快取之後，也必須依照相同的約定運作。若設計上允許透過設定變更 URL 前綴或啟用與停用，也需要告知使用者何時會反映這些變更。

本頁以[套件開發的基礎](/zh-TW/advanced/package-development)為前提，將註冊處理與快取的生命週期分開來思考。官方文件參照 Laravel 13 的預設分支 `13.x`，框架的實作參照最新發行版本 `v13.34.0`。

## loadRoutesFrom 只負責載入檔案

當應用程式實作了 `CachesRoutes` 且 `routesAreCached()` 為真時，`ServiceProvider::loadRoutesFrom()` 不會載入路由檔案。其他情況下，則會 `require` 指定的檔案。

這個方法本身不會加上 URI 或路由名稱的前綴、控制器的命名空間或中介軟體。它也不會公開檔案，或將路由加入既有的快取。

```mermaid theme={null}
flowchart TD
    A["提供者的 boot"] --> B["呼叫 loadRoutesFrom"]
    B --> C{"有路由快取?"}
    C -->|否| D["require 套件的路由檔案"]
    C -->|是| E["略過套件檔案的載入"]
    E --> F["Laravel 的 RouteServiceProvider<br>載入應用程式的快取"]
```

此圖以標準的 Laravel 應用程式為前提。並不存在套件專用的快取，套件的路由也包含在整個應用程式的路由快取中。

<Warning>
  即使將套件內的檔案命名為 `routes/web.php`，光是這樣並不會加上 `web` 中介軟體。由於載入途徑與應用程式端的標準路由檔案不同，請在套件端明確指定所需的中介軟體。
</Warning>

## 將設定與註冊分開

在下列範例中，我們建立一個回傳套件是否可回應的公開端點。前提是已透過 Composer 的 PSR-4 將 `Acme\Courier\` 對應至 `src/`，並以[自動偵測](/zh-TW/advanced/package-discovery)或手動方式註冊提供者。

```php config/courier.php theme={null}
<?php

return [
    'routes' => [
        'enabled' => true,
        'prefix' => 'acme-courier',
    ],
];
```

設定的合併在 `register()` 中進行，路由的載入則在 `boot()` 中進行。請勿將註冊 HTTP 路由的提供者設為 `DeferrableProvider`，因為這樣就無法保證提供者會在需要路由時啟動。

```php src/CourierServiceProvider.php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(__DIR__.'/../config/courier.php', 'courier');
    }

    public function boot(): void
    {
        if (config('courier.routes.enabled')) {
            $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
        }
    }
}
```

這個條件只控制路由的註冊。在同時註冊其他服務或 View 的提供者中，請勿將那些處理放進這個條件內。關於向使用者公開設定的方法，以及合併巢狀設定時的注意事項，請參閱[套件設定的合併與快取](/zh-TW/advanced/package-config-merging)。

```php routes/web.php theme={null}
<?php

use Acme\Courier\Http\Controllers\StatusController;
use Illuminate\Support\Facades\Route;

Route::middleware('web')
    ->prefix(config('courier.routes.prefix'))
    ->name('acme-courier.')
    ->group(function () {
        Route::get('/status', StatusController::class)->name('status');
    });
```

```php src/Http/Controllers/StatusController.php theme={null}
<?php

namespace Acme\Courier\Http\Controllers;

use Illuminate\Http\JsonResponse;

class StatusController
{
    public function __invoke(): JsonResponse
    {
        return response()->json(['available' => true]);
    }
}
```

預設的 URI 為 `/acme-courier/status`，路由名稱為 `acme-courier.status`。只要以 `route('acme-courier.status')` 產生 URL，即使變更 URI 前綴，呼叫端也能使用相同的路由名稱。群組的 `name()` 會直接串接字串，因此結尾的 `.` 也要指定。

<Info>
  `web` 並不能取代認證與授權。此範例是不含機密資訊的公開端點。對於回傳使用者資料的端點，請依照規格另外設置認證中介軟體與授權處理。
</Info>

## 分別防止 URI 與路由名稱的衝突

URI 前綴與路由名稱前綴是不同的機制。只加上其中一個，無法防止另一個發生衝突。

| 對象 | 此範例的設計 | 維護上的注意事項 |
| - | - | - |
| URI | 以 `acme-courier` 為預設值，可透過設定變更 | 選擇不會與使用端應用程式既有 URL 衝突的值 |
| 路由名稱 | 固定為 `acme-courier.` | 使用套件專屬的名稱，並作為 URL 產生的約定加以維持 |
| 控制器 | 使用類別參照 | 不依賴應用程式端的控制器命名空間 |
| 中介軟體 | 明確指定 `web` | 配合目標應用程式的中介軟體結構進行確認 |

`AbstractRouteCollection` 在建立快取用的路由集合時，具有若不同路由使用相同名稱就拋出 `LogicException` 的處理。「一般啟動時能產生 URL」並不能保證可以快取。即使是 URI 不同的 2 個路由，只要名稱相同就會造成問題。

請勿將依註冊順序覆寫使用端應用程式的路由當作套件的擴充方式。如有需要，請提供停用路由的設定，以及使用者可從其他路由呼叫的服務。

## 建立快取時的設定會保留在路由定義中

`RouteCacheCommand` 會先執行 `route:clear`，接著啟動新的應用程式並收集路由。它會將這些路由準備成可序列化的形式，再將編譯結果寫入快取檔案。

此時套件的路由檔案也會被載入，因此前綴與是否註冊取決於**建立快取時的設定**。之後的啟動中，`loadRoutesFrom()` 不會載入檔案，而是使用已快取的路由。

| 變更 | 舊的路由快取殘留時 | 必要的處理 |
| - | - | - |
| 在套件中新增路由 | 新增的路由不會出現 | 重新建立路由快取 |
| 變更 `routes.prefix` | 原本的 URI 仍然存在 | 以新的設定重新建立 |
| 將 `routes.enabled` 變更為 `false` | 快取中的路由不會消失 | 以停用後的設定重新建立 |
| 移除套件 | 可能殘留參照已刪除類別的定義 | 以移除後的結構重新建立 |

<Warning>
  `routes.enabled` 是控制註冊的設定，並非針對每個請求拒絕存取。在舊快取仍殘留的狀態下只停用設定，並不代表已經停止端點。
</Warning>

請勿依使用者或租戶等每個請求都會改變的條件來註冊路由。這些條件會在建立快取時的 CLI 環境中評估。請以穩定的結構註冊路由，並在中介軟體或控制器內的授權中判斷是否允許存取。

### 部署時先確定設定

完成程式碼與設定的更新後，在使用設定快取的結構中，請依下列順序重新建立。請將其納入使用端應用程式的部署處理中。

```bash theme={null}
php artisan config:cache
php artisan route:cache
php artisan route:list --name=acme-courier -vv
```

若在舊的設定快取仍殘留的情況下執行 `route:cache`，路由也會以舊的設定建立。只重新執行 `config:cache` 並不會更新路由快取。使用 `-vv` 也能確認中介軟體群組的內容。

開發中若要在沒有快取的情況下確認行為，請視需要清除兩者。

```bash theme={null}
php artisan config:clear
php artisan route:clear
```

在有快取的啟動中，路由檔案不會被執行。若在其中註冊事件監聽器或容器綁定，行為就會改變，因此請勿讓路由檔案具有路由定義以外的副作用。在使用長時間執行程序的環境中，也請將快取更新後的重新載入納入一般的部署步驟。

## 發行前要確認的組合

除了套件的測試之外，也請在 Laravel 13 的使用端應用程式中確認下列組合。不只是記憶體內的路由註冊，Artisan 啟動新應用程式的途徑也要列入確認對象。

* 在沒有快取的情況下 `/acme-courier/status` 能回應，且路由名稱與中介軟體符合預期。
* `route:cache` 執行成功，在新的啟動中也能以相同的 URI 與路由名稱回應。
* 變更前綴並重新建立快取後，新的 URI 能回應，且舊 URI 的套件路由已不存在。
* 停用並重新建立快取後，`route:list --name=acme-courier` 中不會出現目標路由。
* 與使用端應用程式或其他套件之間，URI 與路由名稱不會衝突。

若也確認殘留舊快取的情況，就能重現使用者回報的「明明改了設定檔，URL 卻沒有變」。請在升級步驟中明確記載快取的重新建立，並將路由名稱與中介軟體的變更也列入相容性的檢討對象。

## 相關頁面

<Columns cols={2}>
  <Card title="路由" icon="route" href="/zh-TW/routing">
    確認路由群組、命名路由與列表顯示的基礎。
  </Card>

  <Card title="套件設定的合併與快取" icon="sliders" href="/zh-TW/advanced/package-config-merging">
    確認考量已公開設定與設定快取的更新步驟。
  </Card>

  <Card title="延遲服務提供者" icon="clock" href="/zh-TW/advanced/deferred-provider">
    確認不應延遲註冊路由之提供者的理由。
  </Card>

  <Card title="套件的版本相容性管理" icon="code-branch" href="/zh-TW/advanced/package-versioning">
    將公開 API 的變更連結至發行方針與持續驗證。
  </Card>
</Columns>

## 參考的一手資料

* [Laravel 官方文件：套件的路由](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/packages.md#routes)
* [Laravel 官方文件：路由](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/routing.md)
* [ServiceProvider：loadRoutesFrom 的實作](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [RouteServiceProvider：快取的載入](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Support/Providers/RouteServiceProvider.php)
* [RouteCacheCommand：在新應用程式中的收集與儲存](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/RouteCacheCommand.php)
* [AbstractRouteCollection：路由名稱重複的偵測](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Routing/AbstractRouteCollection.php)


## Related topics

- [Laravel 套件開發](/zh-TW/advanced/package-development.md)
- [套件設定的合併與快取](/zh-TW/advanced/package-config-merging.md)
- [Laravel 11 以後的應用程式結構](/zh-TW/advanced/app-structure.md)
- [從 Laravel 12 升級到 13 指南](/zh-TW/blog/upgrade-12-to-13.md)
- [從 Laravel 11 升級到 12 指南](/zh-TW/blog/upgrade-11-to-12.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.