Laravelの構成概念 第2回 サービスコンテナ編

## はじめに

前回のライフサイクル編の続きとして、今回はサービスコンテナのご紹介をします。

## シリーズ

- [Laravelの構成概念 第1回 ライフサイクル編](https://qiita.com/ucan-lab/items/5e0a4584c5321a502da7)
- **Laravelの構成概念 第2回 サービスコンテナ編**
- [Laravelの構成概念 第3回 サービスプロバイダ編](https://qiita.com/ucan-lab/items/738abfd650be76333da6)

## 環境

- Laravel 8.5.11

## 参考

- [Laravel 8.x ライフサイクル](https://readouble.com/laravel/8.x/ja/lifecycle.html)
- [Laravel 8.x サービスコンテナ](https://readouble.com/laravel/8.x/ja/container.html)
- [Laravel 8.x サービスプロバイダ](https://readouble.com/laravel/8.x/ja/providers.html)
- [Laravel 8.x ファサード](https://readouble.com/laravel/8.x/ja/facades.html)
  - (今回はあまり触れません)
- [Laravel API 8.x](https://laravel.com/api/8.x)
- [Laravel フレームワーク](https://github.com/laravel/laravel)
- [Laravel コアフレームワーク](https://github.com/laravel/framework)

Laravelの構成概念については公式ドキュメントにより詳しい内容がまとまっているので詳細を知りたい方は上記の公式ドキュメントをご覧ください。

## Laravel サービスコンテナとは

Laravelのサービスコンテナはクラスの依存関係を管理し、依存注入(DI)を実行するための機能です。
イメージとしては、サービス(クラス)を入れておく箱のイメージです。

サービスコンテナは結合と依存解決という二つの機能があります。

- 結合: サービスコンテナにクラスを登録する
- 依存解決: サービスコンテナに登録されたクラスのインスタンスを取り出す

### DI(Dependency Injection)とは

Dependency Injectionは依存性の注入となります。
抽象クラス(interface)を具象クラスに差し替えて実行できる。

要するにクラスのインスタンス生成処理をサービスコンテナに任せて、クラスの疎結合に保つ仕組みです。
疎結合になっていることでテストの時はDBや外部APIを実際に叩かせないように、プログラム実行時にクラスを差し替えできる。

なかなか簡潔に説明するのは難しく、詳細はDI関連のオススメ記事を抜粋したので読んだり、ご自身で調べてみてください。

- [依存性の注入](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5)
- [DI・DIコンテナ、ちゃんと理解出来てる・・？](https://qiita.com/ritukiii/items/de30b2d944109521298f)
- [DIコンテナの本当の使いどころ](https://www.ulsystems.co.jp/topics/025)
- [PHPerKaigi 2021: 今こそ理解するDI（Dependency Injection） / Sho Yamada](https://www.youtube.com/watch?v=SIZiokkbJI4)

#### DIの種類

- コンストラクタインジェクション
- メソッドインジェクション(セッターインジェクション)
- フィールドインジェクション(プロパティインジェクション)
  - PHPでは標準実装されていない

DIの種類は3つありますが、コンストラクタインジェクションだけで十分なので覚えておきましょう。

Laravelのフレームワーク内で実行されるコントローラのルーティングに対応しているメソッドやコンソールコマンドの`handle()`メソッドであればメソッドインジェクションを利用できます。(`Request`クラスなどよく使われます)

## 結合: サービスコンテナにクラスを登録する

結合とはサービスコンテナにインターフェースに対応するクラスを登録することです。
登録自体は次章のサービスプロバイダを使って登録します。

- bind
- singleton
- instance

大きく3種類ありますが、特に理由がない限りはbindで結合します。
他にもコンテキストによる結合、プリミティブの結合、型指定した可変引数の結合、タグ付け、結合の拡張等ありますが、必要に迫られたら検討してみてください。

### bind(バインド、シンプルな結合)

インターフェース名に対応するクラスを登録する。
サービスコンテナから取り出すときに毎回新しいインスタンスを生成する。

### singleton(シングルトンの結合)

インターフェース名に対応するクラスを登録する。
サービスコンテナから初めて取り出すときに新しいインスタンスを生成する。
2回目以降に取り出す時は初回に生成された同じインスタンスが返る。(要は同じインスタンスを使い回しする)

ライフサイクルの記事では、`bootstrap/app.php`で`Kernel`クラスがシングルトン結合されてましたね。

### instance(インスタンスの結合)

インターフェース名に対応するインスタンスを登録する。
予め生成したインスタンスをサービスコンテナに登録します。
取り出す時は登録時のインスタンスが返ります。

## 依存解決: サービスコンテナに登録されたクラスのインスタンスを取り出す

依存解決は結合とは反対にサービスコンテナに登録したクラスのインスタンスを取り出すことです。
`make`メソッドを利用して取り出すパターンと`自動注入(DI)`を利用して取り出すパターンがあります。

特に理由がない限りは `自動注入(DI)` を使って取り出します。

### 自動注入(DI)

基本は[コンストラクタインジェクション](https://readouble.com/laravel/8.x/ja/controllers.html#constructor-injection)を利用します。
Laravelでは[メソッドインジェクション](https://readouble.com/laravel/8.x/ja/controllers.html#method-injection)や[ルートモデル結合](https://readouble.com/laravel/8.x/ja/routing.html#route-model-binding)といった便利機能がありますが、やはりコンストラクタインジェクションだけで良いかなと思います。

## さいごに

以上でサービスコンテナ編は終了です。
サービスコンテナは難しいと抵抗あるかもしれませんが、クラスを登録して取り出すという実はシンプルな機能です。

次回はサービスプロバイダーの記事です。お楽しみに！