【Laravel】 サービスコンテナとは、使い方や解説

### サービスコンテナとは
たくさんのサービスを入れておく箱という認識でOK（クラスをインスタンス化してくれる箱）

### 箱に入れる方法

```php
app()->bind()
```

で箱に登録したり

### 箱の中から取り出す

```php
app()->make()
```

で箱の中から取り出したりをする事ができます。

実際にコードを書いた方が理解が深まると思いますので進めていきましょう。

### 環境
laravel8
Windows11

# サービスコンテナの使い方

## ルーティングの作成

最初にルーティングファイルから作ることにします。

> routes > web.php
> 

```php
use App\Http\Controllers\LifeCycleController;

Route::get('/container', [LifeCycleController::class, 'showServiceContainer']);
```

web.phpに記述する事ができたらコントローラーを作成していきます。

## コントローラーの作成

次にターミナルで以下を実行してコントローラーを作成していきます。

```php
php artisan make:controller LifeCycleController
```

上で作成したLifeCycleControllerは以下の場所に作成されます。

> app > Http > Controllers > LifeCycleController
> 

上のコントローラーが確認できたらOKです！

## サービスコンテナの中身を確認

`app()`のヘルパ関数を使うと中身を確認できるので、`dd()`で`app()`の中身を見てみましょう。

```php
class LifeCycleController extends Controller
{
    public function showServiceContainer()
    {
        dd(app());
    }
}
```

この状態でweb.phpに記述したURLを簡易サーバーで確認すると以下のように`app()`の中身が確認する事ができます。

![20230704_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494399/3a41666a-d0fb-cc1b-9e6f-c019f14cfe56.png)

上の`Illuminate\Foundation\Application`の中にある`bindings:``array:71 [▶]`は、サービスコンテナに登録されているサービスの数を表しています。実際に中身を確認すると様々なサービスがあることが確認できます。

まずは簡単にサービスコンテナにサービスを登録するということをやってみます。

## サービスコンテナにサービスを登録する方法

登録する方法は、

```php
app()->bind('lifeCycle', function()
{
   return 'ライフサイクルのテスト';
});
```

`app()`ヘルパ関数に`bind()`というメソッドで登録する事ができます。

登録した後に呼び出す名前を付ける必要があるので、今回は`bind()`の第一引数に記述されている `lifeCycle` で登録していきます。

`bind()`の第二引数で実行する内容を書いていきます。今回は`ライフサイクルのテスト`という文字を返すという内容でやっていきます。

## 実際にコードを書く

上で説明したようにコントローラーに書いていきます。

> app > Http > Controllers > LifeCycleController
> 

```php
class LifeCycleController extends Controller
{
    public function showServiceContainer()
    {
        app()->bind('lifeCycle', function()
        {
            return 'ライフサイクルのテスト';
        });
        dd(app());
    }
}
```

上のように書くことができたらブラウザの画面で確認してみましょう。以下のように表示されていればOK！

![20230704_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494399/bd884185-46de-9c05-ace6-e4c612926a45.png)


先ほどまでは、上の`Illuminate\Foundation\Application`の中にある`bindings:``array:72 [▶]`は`71`だったのですが`72`に増えていることがわかります。

それでは上で追加したサービスコンテナから取り出してみましょう。

## サービスコンテナから取り出す

サービスコンテナから取り出す場合は

```php
app()->make('lifeCycle')
```

`app()`ヘルパ関数に`make()`として引数に先ほど`bind()`の第一引数で設定した`lifeCycle`としてあげれば取り出す事ができます。

## 実際にコードを書く

上で説明したようにコントローラーに書いていきます。

> app > Http > Controllers > LifeCycleController
> 

```php
class LifeCycleController extends Controller
{
    public function showServiceContainer()
    {
        app()->bind('lifeCycle', function()
        {
            return 'ライフサイクルのテスト';
        });

        $test = app()->make('lifeCycle');　//追加
        dd($test, app());　//ddの中に$testを追加
    }
}
```

上のように書くことができたらブラウザの画面で確認してみましょう。以下のように表示されていればOK！

![20230704_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3494399/ae24241e-b0f8-684d-c6de-d6def3dd7f5d.png)

実際に`bind()`で登録した`ライフサイクルのテスト`という文字が呼び出されているのがわかります。

# 依存関係の解決

サービスコンテナの中でも重要な機能として依存関係の解決という機能があります。

## 依存関係の解決について

依存した２つ以上のクラスを使う時に

以下のように普通のPHPであればそれぞれのクラスを`new`でインスタンス化して読み込み実行する必要があります。

```php
$test = new Test();
$sample = new Sample($test);
$sample->run();
```

ですが、サービスコンテナを使用すれば`app()→baind()`で紐づけて`app()→make()`で使用する際に、依存しているクラスも同時にインスタンス化してくれます。

なので`new`でインスタンス化しなくても使用できるのです。

```php
app()->bind('sample',Sample::class);
$sample = app()->make('sample');
$sample->run();
```

## 使い方の説明

```php
class Sample
{
    public $message;

    public function __construct(Message $message)
    {
        $this->message = $message;
    }

    public function run()
    {
        $this->message->send();
    }
}

class Message
{
    public function send()
    {
        echo('メッセージ');
    }
}
```

２つのクラス`Message`と`Sample`を作成します。

`class Message`は、`echo`でメッセージを表示しているだけ

`class Sample`は、`__construct`で`Message`クラスを読み込んで`run()`メソッドの中でMessageクラスの`send()`メソッドを設定しています。

## 実際にコードを書く

上で作成したコントローラー（LifeCycleController）を使用していきます。

※本来なら１ファイルに１クラスなのですが今回は分かりやすい使い方ということでLifeCycleControllerに複数のクラスを書いています。

### class Message

まずはMessageクラスから書いていきます。

> app > Http > Controllers > LifeCycleController
> 

```php
class Message
{
    public function send()
    {
        echo('メッセージ');
    }
}
```

### class Sample

次にSampleクラスを書いていきます。

> app > Http > Controllers > LifeCycleController
> 

```php
class Sample
{
    public $message;

    public function __construct(Message $message)
    {
        $this->message = $message;
    }

    public function run()
    {
        $this->message->send();
    }
}
```

プロパティで`public $message;`を書いて

インスタンス化する時に`Message`も読み込みたいので`__construct(Message $message)`という書きかたで読み込みます。「これは、DIと呼ばれる仕組みで__construct()の引数にクラスを入れてあげると自動的にインスタンス化してくれます。」

`$this->message`をしてあげるとMessageクラスが`message`に入ります。

`$this->message->send();`と書きMessageクラスの`send()`メソッドを使用できるようにしています。

今回はSampleクラスの方で`__construct()`で初期化する時にMessageクラスも使うというようにしているのでSampleクラスを使う場合には、あらかじめMessageクラスをインスタンス化しておく必要があります。

### class LifeCycleController

> app > Http > Controllers > LifeCycleController
> 

```php
class LifeCycleController extends Controller
{
    public function showServiceContainer()
    {
        app()->bind('lifeCycle', function()
        {
            return 'ライフサイクルのテスト';
        });

        $test = app()->make('lifeCycle');

        app()->bind('sample', Sample::class);　//追加
        $sample = app()->make('sample');　//追加
        $sample->run();　//追加

        dd($test, app());
    }
}
```

`app()->bind()`で紐づける際に、呼び出す名前を引数につける必要があるので、`'sample'`としておきます。今回はクラスを付けるので、`Sample::class`と書くことでSampleクラスを紐づける事ができます。

続いて`app()->make('sample');`として呼び出してSampleの中にある`run()`メソッドを表示させます。

以下のように表示されていればOKです！

```php
出力結果
メッセージ
```

## コントローラー全体のコード

> app > Http > Controllers > LifeCycleController
> 

```php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class LifeCycleController extends Controller
{
    public function showServiceContainer()
    {
        app()->bind('lifeCycle', function()
        {
            return 'ライフサイクルのテスト';
        });

        $test = app()->make('lifeCycle');

        app()->bind('sample', Sample::class);　//追加
        $sample = app()->make('sample');　//追加
        $sample->run();　//追加

        dd($test, app());
    }
}
//以下すべて追加

class Sample
{
    public $message;

    public function __construct(Message $message)
    {
        $this->message = $message;
    }

    public function run()
    {
        $this->message->send();
    }
}

class Message
{
    public function send()
    {
        echo('メッセージ');
    }
}
```

# 以上

上のことを理解することでLaravelに書かれているコードがかなり読みやすくなると思うので覚えておくとかなり良いと思います！