> ## 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のサービスコンテナを使った依存性注入の仕組みと、バインディングの基本を解説します。

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

Laravelのサービスコンテナは、クラスの依存関係を管理し、依存性注入を行うための仕組みです。依存性注入とは、クラスが必要とする依存をコンストラクターや、場合によってはセッターメソッドを通じてクラスへ「注入」することを指します。

次の例を見てみましょう。

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

namespace App\Http\Controllers;

use App\Services\AppleMusic;
use Illuminate\View\View;

class PodcastController extends Controller
{
    /**
     * 新しいコントローラーインスタンスを生成する
     */
    public function __construct(
        protected AppleMusic $apple,
    ) {}

    /**
     * 指定されたポッドキャストの情報を表示する
     */
    public function show(string $id): View
    {
        return view('podcasts.show', [
            'podcast' => $this->apple->findPodcast($id)
        ]);
    }
}
```

この例では、`PodcastController` はApple MusicなどのデータソースからPodcastを取得する必要があります。そこで、Podcastを取得できるサービスを**注入**します。サービスを注入することで、テスト時に`AppleMusic`サービスのモック（ダミー実装）を簡単に差し替えられます。

<Info>
  サービスコンテナを深く理解することは、大規模なLaravelアプリケーションを構築するうえで不可欠です。Laravelコア自体への貢献にも役立ちます。
</Info>

```mermaid theme={null}
flowchart TD
    A["サービスプロバイダ<br>register()"] --> B["サービスコンテナ<br>バインディング登録"]
    B --> C{"解決要求<br>make() / 自動注入"}
    C -- "具体クラス" --> D["リフレクションで<br>自動解決"]
    C -- "インターフェース" --> E["登録済み実装クラス<br>を解決"]
    D --> F["インスタンス生成<br>コンストラクタへ注入"]
    E --> F
```

## ゼロコンフィギュレーション解決

クラスが他の具体クラス（インターフェースではない）にしか依存していない場合、コンテナにその解決方法を教える必要はありません。例えば、次のコードを`routes/web.php`に書いたとします。

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

class Service
{
    // ...
}

Route::get('/', function (Service $service) {
    dd($service::class);
});
```

<Info>
  この例はルートファイル内でクラスを定義していますが、これはデモ用です。実際のアプリケーションでは、サービスクラスは `app/Services` ディレクトリに定義してください。
</Info>

このルートにアクセスすると、Laravelは自動的に`Service`クラスを解決してルートハンドラーへ注入します。設定ファイルを用意しなくても依存性注入の恩恵を受けられます。

コントローラー、イベントリスナー、ミドルウェアなど、Laravelアプリケーションで書くクラスの多くは、コンテナを通じて自動的に依存が注入されます。

## バインディング

### 基本的なバインディング

ほとんどのバインディングは[サービスプロバイダ](/jp/service-providers)内で登録します。サービスプロバイダ内では `$this->app` プロパティを通じてコンテナにアクセスできます。

#### bind

`bind` メソッドを使って、クラスまたはインターフェース名とクロージャを渡してバインディングを登録します。

```php theme={null}
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->bind(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});
```

クロージャの引数としてコンテナ自身を受け取ることができます。これを使ってサブ依存を解決できます。

サービスプロバイダの外でコンテナを操作したい場合は `App` ファサードを使います。

```php theme={null}
use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;

App::bind(Transistor::class, function (Application $app) {
    // ...
});
```

<Info>
  インターフェースに依存しないクラスはコンテナへバインドする必要はありません。コンテナはリフレクションを使ってこれらのオブジェクトを自動的に解決できます。
</Info>

#### singleton

`singleton` メソッドは、クラスまたはインターフェースを一度だけ解決するようにバインドします。一度解決されたシングルトンは、以降のコンテナへの呼び出しで同じインスタンスが返されます。

```php theme={null}
use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->singleton(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});
```

#### instance

既存のオブジェクトインスタンスを `instance` メソッドでコンテナにバインドすることもできます。以降のコンテナへの呼び出しでは常にそのインスタンスが返されます。

```php theme={null}
use App\Services\Transistor;
use App\Services\PodcastParser;

$service = new Transistor(new PodcastParser);

$this->app->instance(Transistor::class, $service);
```

### インターフェースを実装にバインドする

サービスコンテナの強力な機能の一つは、インターフェースを特定の実装にバインドできることです。例えば、`EventPusher` インターフェースと `RedisEventPusher` 実装があるとします。

```php theme={null}
use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;

$this->app->bind(EventPusher::class, RedisEventPusher::class);
```

これにより、コンテナは `EventPusher` の実装が必要なクラスに `RedisEventPusher` を注入するようになります。あとはコンストラクターで `EventPusher` インターフェースをタイプヒントするだけです。

```php theme={null}
use App\Contracts\EventPusher;

/**
 * 新しいクラスインスタンスを生成する
 */
public function __construct(
    protected EventPusher $pusher,
) {}
```

<Tip>
  インターフェースに依存することで、実装を差し替えてもコードを変更する必要がなくなります。これにより、テストや将来の変更が容易になります。
</Tip>

## 自動解決（型ヒントによるDI）

サービスコンテナは、コントローラー、イベントリスナー、ミドルウェアなどのクラスを解決する際に、コンストラクターの型ヒントを見て依存を自動的に注入します。

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

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * 新しいコントローラーインスタンスを生成する
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
}
```

`UserRepository` がインターフェースに依存していなければ、コンテナへの登録は不要です。次のルートにアクセスするだけで、コンテナが自動的に依存を解決してコントローラーに注入します。

## コンテナからの解決

### makeメソッド

`make` メソッドを使って、コンテナからクラスインスタンスを解決できます。

```php theme={null}
use App\Services\Transistor;

$transistor = app()->make(Transistor::class);
```

クラスの依存がコンテナで解決できない場合、`makeWith` メソッドを使って追加の引数を渡すこともできます。

```php theme={null}
$transistor = $this->app->makeWith(Transistor::class, ['id' => 1]);
```

### 自動注入

実際には、`make` メソッドを直接呼び出すことはほとんどありません。コンテナが解決するクラス（コントローラー、イベントリスナー、ミドルウェアなど）のコンストラクターに型ヒントを追加するだけで、コンテナが自動的に注入してくれます。

## ファサードとコンテナの関係

Laravelのファサードは、コンテナ内のオブジェクトへの静的インターフェースを提供します。例えば、`Cache::get()` は内部的にコンテナから `Cache` サービスを取得して呼び出しています。

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

// ファサードを使った呼び出し
Cache::get('key');

// コンテナを直接使った同等の呼び出し
app('cache')->get('key');
```

ファサードはコンテナの便利なラッパーです。テスト時にはファサードをモックに差し替えることもできます。

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

Cache::shouldReceive('get')
    ->once()
    ->with('key')
    ->andReturn('value');
```

## コンストラクターインジェクションの実践例

実際のアプリケーションでの典型的なパターンを見てみましょう。

<Steps>
  <Step title="インターフェースを定義する">
    ```php theme={null}
    <?php

    namespace App\Contracts;

    interface PaymentGateway
    {
        public function charge(int $amount, string $token): bool;
    }
    ```
  </Step>

  <Step title="実装クラスを作成する">
    ```php theme={null}
    <?php

    namespace App\Services;

    use App\Contracts\PaymentGateway;

    class StripePaymentGateway implements PaymentGateway
    {
        public function charge(int $amount, string $token): bool
        {
            // Stripe APIを使った決済処理...
            return true;
        }
    }
    ```
  </Step>

  <Step title="サービスプロバイダでバインドする">
    ```php theme={null}
    use App\Contracts\PaymentGateway;
    use App\Services\StripePaymentGateway;

    $this->app->singleton(PaymentGateway::class, StripePaymentGateway::class);
    ```
  </Step>

  <Step title="コントローラーで注入を受け取る">
    ```php theme={null}
    <?php

    namespace App\Http\Controllers;

    use App\Contracts\PaymentGateway;
    use Illuminate\Http\Request;

    class OrderController extends Controller
    {
        public function __construct(
            protected PaymentGateway $payment,
        ) {}

        public function store(Request $request)
        {
            $this->payment->charge(
                $request->amount,
                $request->payment_token
            );

            // ...
        }
    }
    ```
  </Step>
</Steps>

このパターンにより、決済サービスを`Stripe`から別のプロバイダに切り替える場合も、バインディングを1か所変更するだけで対応できます。

## 次のステップ

<Card title="サービスプロバイダ" icon="plug" href="/jp/service-providers">
  サービスプロバイダを使ってバインディングを登録する方法を学びます。
</Card>
