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

# パッケージのStorageドライバー登録と再解決

> Laravel 13のFilesystemManagerを読み解き、Storage::extend()の登録、ディスクのインスタンス保持、設定変更後の再解決とfakeに依存しない検証を解説します。

パッケージからストレージ機能を提供したいときは、**ドライバーの生成方法を登録すること**と、**利用者のディスクを生成すること**を分けます。プロバイダーの起動だけで外部接続やファイル操作を始めない設計にすると、利用しない環境の起動も壊しにくくなります。

このページはLaravel Framework `v13.37.0` の実装を確認しています。基本操作は[ファイルストレージ](/jp/filesystem)、プロバイダーの読み込みは[パッケージの自動検出](/jp/advanced/package-discovery)を参照してください。

## ドライバー名とディスク名を分ける

`Storage::extend()` の第1引数は**ドライバー名**です。一方、`Storage::disk()` の引数は `filesystems.disks` に定義する**ディスク名**です。同じドライバーを使うディスクを複数定義できます。

| 要素 | このページの例 | 役割 |
| - | - | - |
| ドライバー名 | `acme-archive-local` | ディスクを生成するコールバックの識別子 |
| ディスク名 | `reports`、`exports` | 設定と生成済みインスタンスの識別子 |
| ディスク設定 | `root`、`throw` など | そのディスクの生成時にコールバックへ渡す値 |

`FilesystemManager::resolve()` は、設定の `driver` に対応するカスタム生成処理を探し、あれば組み込みドライバーより先に呼び出します。`local` や `s3` を拡張名に使うと既存の生成処理を置き換えるため、パッケージ固有の名前を選びます。

## プロバイダーでは生成方法だけを登録する

次は登録経路を理解するため、Laravel標準のローカルドライバー生成処理を再利用する最小例です。ローカル保存だけが目的なら通常の `local` で十分です。独自バックエンドを提供する場合は、公式ドキュメントの例のようにFlysystemアダプターを構築し、Laravelの `FilesystemAdapter` で包む処理へ置き換えます。

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

namespace Acme\Archive;

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use InvalidArgumentException;

class ArchiveServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Storage::extend('acme-archive-local', static function (
            Application $app,
            array $config,
        ): FilesystemAdapter {
            if (! is_string($config['root'] ?? null) || $config['root'] === '') {
                throw new InvalidArgumentException('Archive disk requires a root.');
            }

            return $app['filesystem']->createLocalDriver($config);
        });
    }
}
```

このプロバイダーは[通常の方法でパッケージに登録](/jp/advanced/package-discovery)します。HTTP・Artisan・キューワーカーで利用できるよう、`runningInConsole()` では登録を制限しません。また、拡張の登録だけを担当するプロバイダーは[遅延プロバイダー](/jp/advanced/deferred-provider)にしません。

コールバックにはアプリケーションと**解決対象のディスク設定全体**が渡されます。設定の検証やクライアントの生成はこの中で行い、`boot()` ではファイル操作をしません。例の `createLocalDriver()` は生成時にディレクトリを作ることがあるため、登録時と生成時を分けることが重要です。

<Tip>
  `FilesystemManager::extend()` は通常のクロージャの `$this` をManagerへバインドします。プロバイダーの `$this` を使える前提にせず、この例のような `static` クロージャと引数の `$app` を使うと、依存関係が明確になります。
</Tip>

利用者は `config/filesystems.php` の `disks` 配列に設定を追加します。

```php theme={null}
'reports' => [
    'driver' => 'acme-archive-local',
    'root' => storage_path('app/private/reports'),
    'throw' => true,
],
'exports' => [
    'driver' => 'acme-archive-local',
    'root' => storage_path('app/private/exports'),
    'throw' => true,
],
```

利用側はドライバー名ではなくディスク名を指定します。

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

Storage::disk('reports')->put('summary.txt', 'Monthly summary');
```

<Info>
  この例はローカル保存の生成経路だけを示します。`serve` によるファイル配信や一時URLの提供は対象外です。独自アダプターでURL生成、可視性、読み取り専用などをサポートする場合も、それぞれの実装と必要な依存パッケージを確認し、設定キーを渡すだけで有効になると判断しないでください。
</Info>

## 名前付きディスクは生成後に保持される

`disk('reports')` は毎回コールバックを呼びません。Managerの `get()` が、内部の `$disks` 配列にある生成済みインスタンスを優先します。未生成の場合だけ `resolve()` を通り、生成結果がディスク名をキーとして保持されます。

```mermaid theme={null}
flowchart TD
    A["プロバイダーのboot()"] --> B["extend()で生成処理を登録"]
    B --> C["Storage::disk('reports')"]
    C --> D{"reportsは生成済み？"}
    D -->|はい| E["保持しているインスタンスを返す"]
    D -->|いいえ| F["driverを読み、生成処理へ設定を渡す"]
    F --> G["生成結果をreportsとして保持"]
    G --> E
```

したがって、同じプロセスで以下を行っても生成済みディスクは自動更新されません。

* `config()` で `filesystems.disks.reports` を変更する。
* 同じドライバー名に対して `Storage::extend()` をもう一度呼ぶ。

`FilesystemServiceProvider` はManager自体もsingletonとして登録します。長時間動くプロセスでは、リクエストやジョブごとにディスクが必ず作り直されると仮定しないでください。

## 設定変更後は必要なディスクだけ再解決する

同一プロセスで設定を変更して検証する場合は、設定の更新と、保持されたディスクの除去を分けます。

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

$before = Storage::disk('reports');

config(['filesystems.disks.reports.root' => storage_path('app/private/reports-v2')]);
Storage::forgetDisk('reports');

$after = Storage::disk('reports');
```

`forgetDisk()` は指定名をManagerの保持配列から取り除きます。次の `disk()` で現在の設定を読み直します。確認したバージョンの `purge()` も指定名を取り除く実装で、独自バックエンドの接続終了メソッドを呼ぶわけではありません。

<Warning>
  `$before` のように利用側が保持している参照まで書き換わるわけではありません。singletonサービスが古いディスクを保持している場合も同様です。`forgetDisk()` を、すべての参照や別プロセスを一括更新する仕組みとして使わないでください。
</Warning>

本番の設定変更やパッケージ更新では、通常のデプロイに沿って設定キャッシュを再作成し、長時間動くワーカーも再起動します。設定キャッシュと、このプロセス内のディスク保持配列は別物です。

### 一時的な設定にはbuildを使う

名前付きの共有設定を変更したくない場合は、[オンデマンドディスク](/jp/filesystem)を使えます。

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

$disk = Storage::build([
    'driver' => 'acme-archive-local',
    'root' => storage_path('app/private/one-off-export'),
    'throw' => true,
]);
```

`build()` は指定設定で直接 `resolve()` を呼び、結果を名前付きディスクの保持配列へ追加しません。カスタムドライバーの登録は必要ですが、呼び出すたびに生成処理へ進みます。動的な保存先を使う場合も、保存先や接続先を利用者の入力から無制限に選べる設計にはしません。

## fakeとは別に生成経路をテストする

`Storage::fake('reports')` は、Managerの `set()` でそのディスクをテスト用のローカルディスクに置き換えます。アプリケーションのファイル操作テストには便利ですが、**パッケージのカスタム生成処理が正しく登録・実行されることは検証しません**。

[パッケージのテスト環境](/jp/advanced/package-testing)で `ArchiveServiceProvider` を読み込んだうえで、以下を分けて検証します。

| テスト | 確認すること |
| - | - |
| 通常起動 | ディスク未使用なら生成処理内の設定検証やファイル操作が始まらない |
| 名前付きディスク | `reports` と `exports` にそれぞれの設定が渡り、異なるインスタンスになる |
| 同じ名前の再取得 | `disk('reports')` を2回呼ぶと同一インスタンスになる |
| 設定変更 | 設定を変えただけでは同じインスタンス、`forgetDisk()` 後は別インスタンスになる |
| オンデマンド | 同じ設定で `build()` を2回呼んでも別インスタンスになる |
| 不正な設定 | この例では `root` がないディスクの解決時に例外になる |
| 実際の読み書き | テスト専用ディレクトリで保存・取得・削除を検証し、終了時に片付ける |
| 設定キャッシュあり | 新しく起動した検証アプリでもドライバーが登録され、解決できる |

独自の外部バックエンドでは、ネットワークを使わない生成テストと、明示的に準備した検証環境での接続テストを分けます。fakeの成功を実サービスとの互換性の証拠にはしません。

## 長期保守のチェックリスト

* ドライバー名、設定キー、戻り値のAPI、例外や `throw` の扱いを公開仕様として文書化する。
* 標準ドライバーや他パッケージと衝突しない拡張名を使う。
* プロバイダーの起動だけでディスクを解決・使用しない。
* 設定変更時は設定キャッシュ、Managerの保持配列、利用側の参照、別プロセスを区別する。
* Laravelの対応バージョンごとに、生成・再解決・実際のファイル操作を継続して検証する。

## 参照した一次情報

* [Laravel公式ドキュメント: カスタムファイルシステム](https://github.com/laravel/docs/blob/12080c72699b/filesystem.md#custom-filesystems)
* [FilesystemManager: disk・build・resolve・forgetDisk・purge・extend](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Filesystem/FilesystemManager.php)
* [FilesystemServiceProvider: Managerのsingleton登録](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Filesystem/FilesystemServiceProvider.php)
* [Storageファサード: fakeによるローカルディスクへの置換](https://github.com/laravel/framework/blob/v13.37.0/src/Illuminate/Support/Facades/Storage.php)
* [Laravel公式テスト: ディスクの生成・除去・カスタムクロージャ](https://github.com/laravel/framework/blob/v13.37.0/tests/Filesystem/FilesystemManagerTest.php)


## Related topics

- [応用トピック](/jp/advanced/index.md)
- [パッケージのイベントリスナー登録とキャッシュ](/jp/advanced/package-events.md)
- [パッケージのArtisanコマンド登録と依存解決](/jp/advanced/package-commands.md)
- [コアパッケージと独自ドライバー - Feedable](/jp/packages/feedable/core.md)
- [パッケージのサービス解決後フック](/jp/advanced/package-service-resolution.md)


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