3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Laravelでパラメータの異なる複数のサービスを定義する方法

Last updated at Posted at 2021-06-28

はじめに

次のような、コンストラクタでendpointtokenを受け取り、APIの操作をするクラスがあったとします。

<?php

namespace App\Service;

class ApiClient
{
    public function __construct(private string $endpoint, private string $token)
    {}

    public function run()
    {
        ...
    }
}

このクラスを、DIできるようにするには、サービスプロバイダでLaravelのコンテナに登録しますよね。

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use App\Service\ApiClient;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(ApiClient::class, fn () => new ApiClient('https://hoge.example.com', 'xxxxxxxxxx'));
    }
}


呼び出し側
<?php

namespace App\Usecase;

use App\Service\ApiClient;

class FugaUsecase
{
    // DIされる
    public function __construct(private ApiClient $apiClient)
    {}

    public function exec()
    {
        $this->apiClient->run();
    }
}

しかし、異なるendpointtokenApiClientを使いたいケースが出てきたらどうしますか?
ん?ApiClientの中身をコピペしたクラスをもう一つ作る?さらに異なるendpointtokenApiClientを使いたいケースが出てきたら?修正が発生した時は全部のコードを直しますか?

いいえ、実はそれ、Providerの設定だけでできますよ!という話です。

別の呼び出し側
<?php

namespace App\Usecase;

use App\Service\ApiClient;

class FugaUsecase
{
    // endpointとtokenの異なる ApiClient が欲しい!
    public function __construct(private ApiClient $apiClient)
    {}

    public function exec()
    {
        $this->apiClient->run();
    }
}

やり方

サービスコンテナへ登録する時、$this->app->bind()を使用しますが、この第一引数、実はクラス名じゃなくても大丈夫なんです。ここで指定する値は「サービスをコンテナに登録する際のID」として使われます。ここの値をクラス名と一致させることで「型を使ったDI」ができるようになりますが、その機能を使わないのであれば、必ずしもクラス名にする必要はありません。

よって、まず異なるendpointtokenを持つ2種類のApiClient異なるIDでコンテナに登録します。
コンテナからは$this->app->make(<ID>)で登録したサービスを取得することができるので、必要に応じてサービスを取得し、依存性を解決します。

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use App\Service\ApiClient;
use App\Usecase\FugaUsecase;
use App\Usecase\HogeUsecase;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        // 2種類のサービスを登録
        $this->app->bind('api_client.hoge', fn () => new ApiClient('https://hoge.example.com', 'xxxxxxxxxx'));
        $this->app->bind('api_client.fuga', fn () => new ApiClient('https://fuga.example.com', 'yyyyyyyyyy'));

        // コンテナから取得した ApiClient を使ってサービスを登録する
        $this->app->bind(HogeUsecase::class, fn (Application $app) => new HogeUsecase($app->make('api_client.hoge')));
        $this->app->bind(FugaUsecase::class, fn (Application $app) => new FugaUsecase($app->make('api_client.fuga')));
    }
}

また、ApiClientを使い回す必要がないのであれば、次のように依存性解決と同時にサービスの定義を行うこともできます。

app/Providers/AppServiceProvider.php
<?php

namespace App\Providers;

use App\Service\ApiClient;
use App\Usecase\FugaUsecase;
use App\Usecase\HogeUsecase;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(
            HogeUsecase::class,
            function (Application $app) {
                $client = $app->make(ApiClient::class, ['endpoint' => 'https://hoge.example.com', 'token' => 'xxxxxxxxxx']);
                return new HogeUsecase($client);
            }
        );

        $this->app->bind(
            FugaUsecase::class,
            function (Application $app) {
                $client = $app->make(ApiClient::class, ['endpoint' => 'https://fuga.example.com', 'token' => 'yyyyyyyyyy']);
                return new FugaUsecase($client);
            }
        );
    }
}

終わりに

今回は、重複コードを発生させずに、パラメータの異なる複数のサービスを定義する方法を紹介しました。自前で依存性を管理したり、サービスプロバイダのあまり使わない機能を使ったりするので、初心者には少しハードルが高いかもしれません。

しかし「パラメータの異なるクラスを沢山作りたいとき」や「対象のサービスがかなり大きいクラス」である場合は重複コードを作るよりも、大きなメリットになるので、覚えておいて損はない機能だと思います。使えるタイミングがあれば是非使ってみてください。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?