🍣

【PHP】Laravelのサービスコンテナ・サービスプロバイダ

2024/02/04に公開

Laravel(PHP言語)を使って6年ぶりくらいに開発することになってしまい、色々思い出している最中にサービスコンテナ?サービスプロバイダ??ってなったので、自分なりに調べてみた備忘録を下記にまとめました。

サービスコンテナ・サービスプロバイダとは

サービスコンテナ

サービスコンテナは、アプリケーション内のクラスの依存関係とその管理を担うLaravelの機能です。依存性の注入(DI: Dependency Injection)をサポートし、アプリケーション内で必要となるクラスのインスタンスを生成し、適切な依存関係を自動的に「注入」します。これにより、コードのテストが容易になり、結合度が低く保守しやすいコードを実現できます。

サービスプロバイダ

サービスプロバイダは、サービスコンテナにどのようにしてサービス(クラス、ツール、値など)を登録するかを定義するクラスです。Laravelのアプリケーション起動時に、サービスプロバイダが登録され、その中でサービスコンテナにサービスをバインドします。これにより、アプリケーションの起動と初期化プロセスをカスタマイズできます。

サービスコンテナの3つの役割

サービスコンテナの役割は大きく分けて3つに分けられます。

1.依存関係の解決

サービスコンテナは、アプリケーションが必要とするクラスやオブジェクトの依存関係を解決するために使用されます。クラスが別のクラスを必要とする場合、サービスコンテナは自動的に必要な依存関係をインスタンス化し、それをクラスに提供します。

2.依存性の注入の実装

サービスコンテナを使用することで、依存性の注入(DI)を簡単に実装できます。これは、クラスのコンストラクタやメソッドを通じて依存関係を提供する手法であり、クラスの柔軟性を高め、テストを容易にします。

3.サービスの登録と管理

サービスプロバイダを介してサービスコンテナにサービスを登録することで、アプリケーション全体でサービスを管理し、アクセスできるようにします。これにより、アプリケーションの設定や初期化プロセスをカスタマイズできます。

ここまでの記事を読んだ振り返り

ここまでの記事を読んで理解できた人はすごいです!素晴らしい!!
でも、ほとんどの人は何を説明しているのかさっぱりですよね...。
では、具体的に概念を覚えるのではなくコードを使って理解してみましょう。

事例としてメール送信機能を実装してみる。

ステップ 1: 依存関係の作成

まずは、依存関係となるクラスを作成します。
例として、メール送信機能を担うMailerクラスを考えてみましょう。

app/Mail/Mailer.php
namespace App\Mail;

class Mailer {
    public function send($to, $message) {
        // メール送信のロジックをここに記述
        echo "メールを送信: 宛先 - {$to}, メッセージ - {$message}";
    }
}

ステップ 2: サービスプロバイダの作成

Laravelでは、サービスプロバイダを通じて依存関係をサービスコンテナに登録します。新しいサービスプロバイダを作成するには、php artisan make:providerコマンドを使用します。
ここではMailServiceProviderという名前を使います。

コマンド1
php artisan make:provider MailServiceProvider

ステップ 3: 依存関係の登録

次に、新しく作成したMailServiceProviderをLaravelアプリケーションに登録する必要があります。これを行うには、config/app.php内のproviders配列にMailServiceProviderを追加します。

app/Providers/MailServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Mail\Mailer;

class MailServiceProvider extends ServiceProvider {
    public function register() {
        $this->app->bind('App\Mail\Mailer', function ($app) {
            return new Mailer();
        });
    }
}

ステップ 4: サービスプロバイダの登録

次に、新しく作成したMailServiceProviderをLaravelアプリケーションに登録する必要があります。これを行うには、config/app.php内のproviders配列にMailServiceProviderを追加します。

config/app.php
'providers' => [
    // 他のサービスプロバイダ...

    App\Providers\MailServiceProvider::class,
],

ステップ 5: 依存関係の注入

最後に、Mailerクラスを使用したいクラス内で依存関係を注入します。例として、ユーザー登録後にウェルカムメールを送信するUserControllerを考えてみましょう。

app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Mail\Mailer;

class UserController extends Controller {
    protected $mailer;

    // Mailerクラスの依存関係をコンストラクタインジェクションを通じて注入
    public function __construct(Mailer $mailer) {
        $this->mailer = $mailer;
    }

    public function register() {
        // ユーザー登録のロジック...

        // メール送信
        $this->mailer->send('user@example.com', 'ウェルカムメールの内容');

        // その他の登録後の処理...
    }
}

この例では、UserControllerのコンストラクタにMailerクラスをタイプヒントとして指定することで、Laravelのサービスコンテナが自動的にMailerクラスのインスタンスを$mailerプロパティに注入します。

これで、サービスプロバイダを使って依存関係を登録し、依存関係の注入を実装するプロセスが完了しました。

ここまで読んでも分からない人のための、例え話を使って説明してみます。

ステップ 1: 依存関係の作成

あなたが建築家で、新しい家を建てるプロジェクトを始めることにしました。この家を建てるためには、まず設計図(Mailerクラスの定義)が必要です。設計図がなければ、どのような家を建てるか、どんな材料が必要かがわかりません。

ステップ 2: サービスプロバイダの作成

あなたはこのプロジェクトを実行するために、工務店(サービスプロバイダ)を選びます。工務店は、建設プロジェクトを管理し、必要な工事を計画し、実行する責任があります。

ステップ 3: 依存関係の登録

工務店は、このプロジェクトに必要なすべての職人や材料をリストアップします。例えば、大工、電気工、水道工など(Mailerクラスのような依存関係)をプロジェクトに登録し、彼らがいつ、どのように仕事をするかを計画します。

ステップ 4: サービスプロバイダの作成

この工務店があなたのプロジェクトに正式に契約され、プロジェクトの実行者として登録されます。これにより、プロジェクトのすべての管理と実行がこの工務店に委ねられます。

ステップ 5: サービスプロバイダの作成

実際の建設が始まると、工務店は計画に従って各職人を現場に呼び出し、必要な作業を行わせます。例えば、大工が構造体を建て、その後電気工が配線を行い、水道工が配管を行います。各職人(依存関係)は、家(UserControllerクラス)が完成するために必要な特定の作業を提供します。

この例えでは、建築プロジェクトの各ステップが、ソフトウェア開発における依存関係の注入プロセスの各ステップに対応しています。建築家が自分で全ての作業を行うのではなく、各専門職(依存関係)が必要な作業を提供することで、効率的かつ専門的にプロジェクトを完成させることができます。これは、ソフトウェア開発において、各クラスが自身で依存関係を作成・管理するのではなく、フレームワークやサービスコンテナが必要な依存関係を提供し、クラスに注入することによって、コードの再利用性、保守性、テストの容易さが向上するのと同じ原理です。

依存関係を注入する他の方法もあります。(余談ですが...)

Laravelを使用する際、コンストラクタインジェクションは非常に一般的な依存関係の注入(Dependency Injection、DI)の手法ですが、依存関係を注入する他の方法もあります。コンストラクタインジェクション以外にも、セッターインジェクションやプロパティインジェクションなどの手法がありますが、Laravelでは主にコンストラクタインジェクションが推奨されています。それでも、特定のケースでは他の手法を使用する場面もあります。

サービスコンテナを利用する3つのメリット

柔軟性

依存関係をクラス外部から注入することで、クラスの実装を変更せずに依存するクラスの実装を変更することができます。

テスト容易性

依存関係をモック(テスト用のダミーオブジェクト)に置き換えることが容易になり、単体テストが行いやすくなります。

再利用性

同じ依存関係を持つクラス間でコードの重複を減らすことができます。

Discussion