> ## 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のcallAfterResolving()を読み解き、未解決・解決済みのサービスを拡張する方法と、singleton・通常バインディング・再登録時の注意点を解説します。

パッケージからLaravelのサービスに機能を追加するとき、使われるまでインスタンスを作らず、すでに作られている場合にも設定を反映したいことがあります。サービスプロバイダーの `callAfterResolving()` は、この2つを組み合わせるためのprotectedメソッドです。

このページではLaravel Framework `v13.35.0` の実装を確認し、[パッケージ開発の基礎](/jp/advanced/package-development)にある `boot()` からの拡張を掘り下げます。アプリケーション全体の起動完了を待つフックではなく、指定したサービスの解決に対するフックです。

## 未解決なら待ち、解決済みなら今も実行する

`ServiceProvider::callAfterResolving()` は、次の2段階の処理を行います。

1. コンテナの `afterResolving()` にコールバックを登録する。
2. `resolved()` がtrueなら `make()` で取得し、その場でもコールバックを呼ぶ。

未解決の場合、このメソッド自身は対象を `make()` しません。通常の解決では、コンテナがオブジェクトを構築し、extenderを適用し、`resolving` コールバックを呼んだ後に `afterResolving` コールバックを呼びます。

```mermaid theme={null}
flowchart TD
    A["boot()でcallAfterResolving()"] --> B["afterResolvingに登録"]
    B --> C{"対象はresolved()?"}
    C -->|No| D["今は対象を生成しない"]
    D --> E["後で対象が解決される"]
    E --> F["登録したコールバックを実行"]
    C -->|Yes| G["make()で対象を取得"]
    G --> H["その場でもコールバックを実行"]
```

| 方法 | 登録時に未解決 | 登録時に解決済みのsingleton |
| - | - | - |
| `$this->app->make()` して直接設定 | その場で生成する | 既存インスタンスを設定する |
| `$this->app->afterResolving()` | 今後の解決を待つ | 登録だけでは既存インスタンスを設定しない |
| `$this->callAfterResolving()` | 今後の解決を待つ | 既存インスタンスにもその場で設定する |

通常のsingleton取得では、コンテナは保存済みインスタンスを早期returnするため、取得のたびに `afterResolving` が発火するわけではありません。単に `afterResolving()` を登録するだけでは、登録前に作られたsingletonへの設定が抜ける点が重要です。

## バリデーションFactoryにルールを追加する

パッケージ独自の文字列ルールを提供する例として、`validator` の解決後にルールを登録します。公式の `ValidationServiceProvider` はこのキーをsingletonとして登録しており、プロバイダー自体は遅延読み込みに対応しています。

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

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Factory;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->callAfterResolving('validator', function (Factory $factory): void {
            $factory->extend(
                'courier_tracking_code',
                static function ($attribute, $value, $parameters, $validator): bool {
                    return is_string($value)
                        && preg_match('/\ACR-[0-9]{8}\z/', $value) === 1;
                },
                'The :attribute must be a valid courier tracking code.',
            );
        });
    }
}
```

利用アプリケーションでは、通常のルールと組み合わせて使います。

```php theme={null}
$request->validate([
    'tracking_code' => ['required', 'string', 'courier_tracking_code'],
]);
```

この例の `extend()` は、`Illuminate\Validation\Factory` のルール登録APIです。後述するコンテナの `extend()` とは別のメソッドです。通常のカスタムルールは空値などで実行されない場合があるため、必須性は `required` で指定します。ルール自体の詳しい設計は[カスタムバリデーションルール](/jp/advanced/custom-validation-rules)を参照してください。

`Factory::extend()` は同じルール名の配列要素を上書きするので、この例のように同じ処理を再登録してもエントリーが増えません。ただし、別パッケージのルールを上書きしないよう、公開するルール名にはパッケージ固有の接頭辞を付けます。

<Info>
  コールバックが実行されるのはFactoryの解決時です。ルールの検証用クロージャが実行されるのは、後でValidatorが対象の値を検証するときです。フックの登録時に入力値を検証するわけではありません。
</Info>

## 対象のキーと解決済み判定をそろえる

コンテナの通常の解決イベントでは、登録したキーとの一致だけでなく、取得したオブジェクトの型に一致するコールバックも選ばれます。一方、`callAfterResolving()` がその場で使う `resolved($name)` は、別名を正規化したキーについて、解決済みフラグまたは保存済みインスタンスがあるかを調べます。生成済みオブジェクトをすべて検索する処理ではありません。

そのため、あるインターフェースやクラスが解決済みだからといって、別のキーでの `resolved()` も必ずtrueになるとは限りません。対象サービスの実際のバインディングとaliasを確認し、解決済みケースも同じキーでテストします。上の例では、公式プロバイダーが登録する `validator` を指定しています。

また、`resolved()` は「過去に解決された」という判定も含みます。現在のインスタンスが必ず残っている、という意味ではありません。

## 「一度だけ実行」を保証するAPIではない

`callAfterResolving()` は、コールバックを登録したままにします。その場で実行された後も、将来オブジェクトが新しく解決されれば登録済みコールバックが実行されます。さらに、メソッドを再度呼べばコールバック自体が追加されます。

特に、通常の非共有バインディングをすでに解決した場合は注意が必要です。確認した実装では次のようになります。

1. 新しいコールバックを登録する。
2. `resolved()` がtrueなので `make()` を呼ぶ。
3. `make()` が新しいオブジェクトを作り、その解決イベントでコールバックが実行される。
4. `make()` から戻った同じオブジェクトに、その場のコールバックも実行される。

この経路では、今回登録したコールバックが同じオブジェクトに2回適用されます。singletonの取得だけを想定した設計を、通常バインディングにそのまま流用しないでください。

<Warning>
  コールバック内でメール送信、外部API呼び出し、課金、無条件のリスナー追加などを行わないでください。サービスへの設定を冪等に適用する用途に絞り、再登録や再解決で副作用が重複しない設計にします。
</Warning>

## オブジェクトの置換には別のAPIを使う

`afterResolving` のコールバックの戻り値は、コンテナが返すオブジェクトの置換には使われません。デコレーターを返してサービスそのものを差し替えたい場合は、コンテナの `extend()` を検討します。このAPIのクロージャは変更後のサービスを返す契約です。

同様に、`callAfterResolving()` は別オブジェクトにすでに保存された依存関係をすべて更新する仕組みでもありません。再バインド時の依存関係の更新が目的なら、公式ドキュメントの `rebinding()` と対象クラスの設計を確認します。

サービスを本当に遅延読み込みするプロバイダーの要件は[DeferrableProvider](/jp/advanced/deferred-provider)を参照してください。フックを使うことと、そのフックを登録するプロバイダー自身を遅延読み込みにすることは別です。

## 更新時に確認する組み合わせ

パッケージのリリース前には、通常の起動順だけでなく、先に別プロバイダーがサービスを利用した場合も検証します。

| ケース | 確認すること |
| - | - |
| 対象が未解決の状態でフックを登録 | 登録だけでは対象を生成せず、初回解決で設定が反映される |
| singletonを先に解決してから登録 | 同じインスタンスにその場で設定が反映される |
| 設定済みsingletonを通常の `make()` で再取得 | 同じインスタンスが返り、解決コールバックが重複実行されない |
| 過去に解決した通常バインディングに登録 | 登録時の `make()` と明示呼び出しの2回適用でも壊れない |
| 複数回のフック登録やインスタンス再生成 | 同じルール・リスナー・パスなどが意図せず蓄積しない |
| Laravelの対応バージョンを更新 | バインディングキー、共有性、フックの実行経路を再確認する |

アプリケーション全体で一度だけ使ったことを静的フラグで記録すると、新しいコンテナやテストで作り直されたサービスへの設定が抜ける可能性があります。設定の冪等性は、できるだけ対象オブジェクトや登録するキーの単位で確保します。

## 関連ページ

<Columns cols={2}>
  <Card title="パッケージビューの上書きと更新" icon="eye" href="/jp/advanced/package-views">
    loadViewsFrom()が解決後フックを使ってビュー名前空間を登録する例を確認します。
  </Card>

  <Card title="パッケージ翻訳の上書きと更新" icon="language" href="/jp/advanced/package-translations">
    Translatorの名前空間登録と、読み込み済み翻訳の扱いを確認します。
  </Card>
</Columns>

## 参照した一次情報

* [Laravel公式ドキュメント: パッケージ開発](https://github.com/laravel/docs/blob/13.x/packages.md)
* [Laravel公式ドキュメント: Container events・Extending bindings・Rebinding](https://github.com/laravel/docs/blob/13.x/container.md#container-events)
* [Laravel公式ドキュメント: カスタムバリデーションルール](https://github.com/laravel/docs/blob/13.x/validation.md#custom-validation-rules)
* [ServiceProvider: callAfterResolving()とリソース登録での利用](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [Container: resolved()・resolve()・afterResolving()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Container/Container.php)
* [ValidationServiceProvider: validatorのsingleton登録](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/ValidationServiceProvider.php)
* [Validation Factory: extend()によるルール登録](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Validation/Factory.php)


## Related topics

- [Laravelパッケージ開発](/jp/advanced/package-development.md)
- [応用トピック](/jp/advanced/index.md)
- [遅延サービスプロバイダー](/jp/advanced/deferred-provider.md)
- [パッケージのマイグレーション公開と更新](/jp/advanced/package-migrations.md)
- [Lotteryクラス](/jp/advanced/lottery.md)


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