サービスコンテナとは
Laravelのサービスコンテナは、クラスの依存関係を管理し、依存性注入を行うための仕組みです。依存性注入とは、クラスが必要とする依存をコンストラクターや、場合によってはセッターメソッドを通じてクラスへ「注入」することを指します。 次の例を見てみましょう。PodcastController はApple MusicなどのデータソースからPodcastを取得する必要があります。そこで、Podcastを取得できるサービスを注入します。サービスを注入することで、テスト時にAppleMusicサービスのモック(ダミー実装)を簡単に差し替えられます。
サービスコンテナを深く理解することは、大規模なLaravelアプリケーションを構築するうえで不可欠です。Laravelコア自体への貢献にも役立ちます。
ゼロコンフィギュレーション解決
クラスが他の具体クラス(インターフェースではない)にしか依存していない場合、コンテナにその解決方法を教える必要はありません。例えば、次のコードをroutes/web.phpに書いたとします。
この例はルートファイル内でクラスを定義していますが、これはデモ用です。実際のアプリケーションでは、サービスクラスは
app/Services ディレクトリに定義してください。Serviceクラスを解決してルートハンドラーへ注入します。設定ファイルを用意しなくても依存性注入の恩恵を受けられます。
コントローラー、イベントリスナー、ミドルウェアなど、Laravelアプリケーションで書くクラスの多くは、コンテナを通じて自動的に依存が注入されます。
バインディング
基本的なバインディング
ほとんどのバインディングはサービスプロバイダ内で登録します。サービスプロバイダ内では$this->app プロパティを通じてコンテナにアクセスできます。
bind
bind メソッドを使って、クラスまたはインターフェース名とクロージャを渡してバインディングを登録します。
App ファサードを使います。
インターフェースに依存しないクラスはコンテナへバインドする必要はありません。コンテナはリフレクションを使ってこれらのオブジェクトを自動的に解決できます。
singleton
singleton メソッドは、クラスまたはインターフェースを一度だけ解決するようにバインドします。一度解決されたシングルトンは、以降のコンテナへの呼び出しで同じインスタンスが返されます。
singletonIf メソッドを使うと、指定した型に対するバインディングがまだ登録されていない場合のみシングルトンバインディングを登録できます。
Singleton属性
クラスやインターフェースに#[Singleton] 属性を付けることでも、一度だけ解決されるようコンテナに指示できます。
スコープ付きシングルトンのバインド
scoped メソッドは、クラスまたはインターフェースをLaravelのリクエスト/ジョブのライフサイクル内で一度だけ解決するようにバインドします。singleton メソッドと似ていますが、scoped メソッドで登録されたインスタンスは、Laravel Octane ワーカーが新しいリクエストを処理するときや、キューワーカーが新しいジョブを処理するときなど、Laravelアプリケーションが新しい「ライフサイクル」を開始するたびに破棄されます。
scopedIf メソッドを使うと、指定した型に対するバインディングがまだ登録されていない場合のみスコープ付きバインディングを登録できます。
Scoped属性
クラスやインターフェースに#[Scoped] 属性を付けることでも、リクエスト/ジョブのライフサイクル内で一度だけ解決されるようコンテナに指示できます。
instance
既存のオブジェクトインスタンスをinstance メソッドでコンテナにバインドすることもできます。以降のコンテナへの呼び出しでは常にそのインスタンスが返されます。
インターフェースを実装にバインドする
サービスコンテナの強力な機能の一つは、インターフェースを特定の実装にバインドできることです。例えば、EventPusher インターフェースと RedisEventPusher 実装があるとします。
EventPusher の実装が必要なクラスに RedisEventPusher を注入するようになります。あとはコンストラクターで EventPusher インターフェースをタイプヒントするだけです。
Bind属性
Laravelはさらに便利なBind 属性も提供しています。インターフェースにこの属性を付けることで、そのインターフェースが要求されたときにどの実装を自動的に注入するかをLaravelに伝えられます。Bind 属性を使う場合、サービスプロバイダで追加の登録処理を行う必要はありません。
さらに、インターフェースに複数の Bind 属性を配置することで、環境ごとに異なる実装を注入するよう設定することもできます。
自動解決(型ヒントによるDI)
サービスコンテナは、コントローラー、イベントリスナー、ミドルウェアなどのクラスを解決する際に、コンストラクターの型ヒントを見て依存を自動的に注入します。UserRepository がインターフェースに依存していなければ、コンテナへの登録は不要です。次のルートにアクセスするだけで、コンテナが自動的に依存を解決してコントローラーに注入します。
コンテナからの解決
makeメソッド
make メソッドを使って、コンテナからクラスインスタンスを解決できます。
makeWith メソッドを使って追加の引数を渡すこともできます。
自動注入
実際には、make メソッドを直接呼び出すことはほとんどありません。コンテナが解決するクラス(コントローラー、イベントリスナー、ミドルウェアなど)のコンストラクターに型ヒントを追加するだけで、コンテナが自動的に注入してくれます。
ファサードとコンテナの関係
Laravelのファサードは、コンテナ内のオブジェクトへの静的インターフェースを提供します。例えば、Cache::get() は内部的にコンテナから Cache サービスを取得して呼び出しています。
コンストラクターインジェクションの実践例
実際のアプリケーションでの典型的なパターンを見てみましょう。1
インターフェースを定義する
2
実装クラスを作成する
3
サービスプロバイダでバインドする
4
コントローラーで注入を受け取る
Stripeから別のプロバイダに切り替える場合も、バインディングを1か所変更するだけで対応できます。
次のステップ
サービスプロバイダ
サービスプロバイダを使ってバインディングを登録する方法を学びます。