LaravelのService Containerについて
web-application-framework laravel
LaravelのService Containerについて調査しました。
参考ページ
環境
- Laravel 11
もくじ
Service Containerとは
公式ページによると、Service ContainerはPHPのクラスの依存関係を管理し、コンストラクタやセッターを通して依存関係を注入するツールとのことです。
上記の説明だけだとよく分からなかったのですが、こちらのページを参考にさせていただくと、あるクラスのインスタンスを作成したいときに、依存関係を意識しないで作成できるツールのようです。
例えば以下の2つのクラスが定義されているとします。
class ClassA
{
public function __construct(ClassB $class_b)
{
}
}
class ClassB
{
public function __construct()
{
}
}
通常ClassAのインスタンスを作成したいとき、以下のようにClassBのインスタンスも作成する必要があります。
$class_b = new ClassB();
$class_a = new ClassA($class_b);
LaravelのService Containerを使うと、以下のようにClassBのインスタンスの作成を意識せずにClassAのインスタンスを作成することができます。このとき内部ではClassBのインスタンスも作成されます。
$class_a = App::make(ClassA::class);
上記では make
関数でインスタンスを作成していますが、通常はControllerなどの引数に指定すると、自動でインスタンスを作成してくれます。
例えば以下のようにTestControllerのコンストラクタの引数にClassAを指定すると、TestControllerのインスタンス作成時に自動でClassAのインスタンスを作成してくれます。
class TestController extends Controller
{
public function __construct(ClassA $class_a)
{
}
}
依存関係の解決方法を定義する
クラスに依存関係が無かったり、他のクラスにのみ依存する場合、Service Containerは自動で依存関係を解決します。(上記の例など)
しかし、依存関係の解決方法を自分で定義することもできます。
例えば以下のクラスが定義されているとします。
class ClassA
{
public function __construct(int $arg)
{
}
}
コンストラクタにintの引数があるため、Service Containerは自動で依存関係を解決できません。
そこで必ず $arg
に1を入れてClassAのインスタンスを作成するように、解決方法を以下のように定義します。
App::bind(ClassA::class, function () {
return new ClassA(1);
});
これによって $arg
に1が入ったClassAのインスタンスが自動で作成されるようになります。
わかりやすいようにClassAのコンストラクタにログを入れ、make
関数でインスタンスを作成します。
class ClassA
{
public function __construct(int $arg)
{
Log::info("arg = {$arg}");
}
}
$class_a = App::make(ClassA::class);
実行すると、以下のようなログが出力されます。
arg = 1
Service Providerで依存関係の解決方法を定義する
公式ページによると、依存関係の解決方法はService Providerの register
関数で定義すると良いみたいです。
Service ProviderはLaravelアプリケーションの様々なもの(Service Containerの依存関係の解決方法やevent listenerなど)を登録するところです。
試しにService Providerに依存関係の解決方法を定義してみます。
Service Providerを作成します。
以下のコマンドを実行して、TestServiceProviderを作成します。
php artisan make:provider TestServiceProvider
ClassAとClassBを作成し、TestServiceProviderに依存関係の解決方法を定義します。
TestServiceProvider.php
:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;
class TestServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
// 依存関係の解決方法の定義を追加
$this->app->bind(ClassB::class, function () {
return new ClassB(1);
});
}
/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}
// ClassAを追加
class ClassA
{
public function __construct(ClassB $class_b)
{
Log::info('ClassA instance is created.');
}
}
// ClassBを追加
class ClassB
{
public function __construct(int $arg)
{
Log::info("ClassB instance is created. arg = {$arg}");
}
}
ClassAのインスタンスを作成するため、適当なRouteのクロージャの引数にClassAを指定します。
web.php
などに以下を定義します。
Route::get('/', function (ClassA $class_a) {});
ブラウザなどでルートにアクセスすると、以下のようなログが出力されます。
ClassB instance is created. arg = 1
ClassA instance is created.
Service ProviderでService Containerの依存関係の解決方法を定義しました。