> ## 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のプレフィックスや有効・無効を設定で変更できる設計では、その変更をいつ反映するかも利用者に案内します。

このページでは[パッケージ開発の基礎](/jp/advanced/package-development)を前提に、登録処理とキャッシュのライフサイクルを分けて考えます。公式ドキュメントはLaravel 13のデフォルトブランチ `13.x`、フレームワークの実装は最新リリースの `v13.34.0` を参照しています。

## loadRoutesFromはファイルを読み込むだけ

`ServiceProvider::loadRoutesFrom()` は、アプリケーションが `CachesRoutes` を実装し、`routesAreCached()` が真の場合にはルートファイルを読み込みません。それ以外の場合は、指定ファイルを `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/` へ対応させ、プロバイダーを[自動検出](/jp/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');
        }
    }
}
```

この条件はルートの登録だけを制御します。他のサービスやビューの登録もあるプロバイダーでは、それらをこの条件の内側に入れないようにします。設定を利用者へ公開する方法や、ネストした設定のマージに関する注意点は[パッケージ設定のマージとキャッシュ](/jp/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="/jp/routing">
    ルートグループ、名前付きルート、一覧表示の基本を確認します。
  </Card>

  <Card title="パッケージ設定のマージとキャッシュ" icon="sliders" href="/jp/advanced/package-config-merging">
    公開済み設定と設定キャッシュを考慮した更新手順を確認します。
  </Card>

  <Card title="遅延サービスプロバイダー" icon="clock" href="/jp/advanced/deferred-provider">
    ルートを登録するプロバイダーを遅延させない理由を確認します。
  </Card>

  <Card title="パッケージのバージョン互換性管理" icon="code-branch" href="/jp/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パッケージ開発](/jp/advanced/package-development.md)
- [応用トピック](/jp/advanced/index.md)
- [パッケージ設定のマージとキャッシュ](/jp/advanced/package-config-merging.md)
- [パッケージのマイグレーション公開と更新](/jp/advanced/package-migrations.md)
- [パッケージ自動検出の内部構造](/jp/advanced/package-discovery.md)


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