💡
Laravelのサービスコンテナをもっと使いこなしてみる! bindメソッドのクロージャに引数を指定してみた
はじめに
Laravelのサービスコンテナにあるbindメソッドのクロージャに何か引数を指定できないか調べてみたところ、指定できたので、その結果を書きます。
環境
- PHP 8.3.6
- Laravel 11.7.0
サンプルコード
今回の調査につかった適当なサンプルです。
namespace App;
interface InterfaceXX
{
public function get(): int;
}
class ClassA implements InterfaceXX
{
public function get(): int
{
return 10;
}
}
class ClassB implements InterfaceXX
{
public function get(): int
{
return 100;
}
}
本題
いきなり結果ですが、サンプルコードを使って次のようなコードが書けることがわかりました。
app/Providers/AppServiceProvider.php
use App\ClassA;
use App\ClassB;
use App\InterfaceXX;
public function register(): void
{
$this->app->bind(InterfaceXX::class, function ($app, $parameter) {
if ($parameter['flag'] === true) {
return new ClassA();
} else {
return new ClassB();
}
});
}
% php artisan tinker
Psy Shell v0.12.3 (PHP 8.3.6 — cli) by Justin Hileman
> use App\InterfaceXX;
> $class = app()->make(InterfaceXX::class, ['flag' => true]);
= App\ClassA {#5060}
> $class->get();
= 10
> $class = app()->make(InterfaceXX::class, ['flag' => false]);
= App\ClassB {#5105}
> $class->get();
= 100
なぜできるのか
色々省略しちゃいますが、makeメソッドを実行すると、resolveメソッドを呼びます。
resolveメソッドの処理の中でbuildメソッドが呼ばれ、
buildメソッドの中でクロージャの第2引数に何か渡していることがわかります。
第2引数に指定している、getLastParameterOverrideメソッドをみると配列を返すことがわかります。
$this->with
というものが、resolveメソッドの引数の$parametersを設定している。
つまり、makeメソッドの第2引数の配列が設定されるので、サービスコンテナのbindメソッドの引数に指定したクロージャの引数になるわけです。
makeメソッドの第2引数はなぜあるのか?
つぎのようなbindメソッドにクロージャではなくクラスを指定した場合、コンストラクタの引数を任意の値を指定できます。
namespace App;
class ClassC implements InterfaceXX
{
public function __construct(private int $num)
{
}
public function get(): int
{
return $this->num;
}
}
app/Providers/AppServiceProvider.php
use App\ClassC;
use App\InterfaceXX;
public function register(): void
{
$this->app->bind(InterfaceXX::class, ClassC::class);
}
% php artisan tinker
Psy Shell v0.12.3 (PHP 8.3.6 — cli) by Justin Hileman
> use App\InterfaceXX;
> $class = app()->make(InterfaceXX::class, ['num' => 1000]);
= App\ClassC {#5103}
> $class->get();
= 1000
まとめ
小ネタでした。
Laravelに詳しくはなれたけど活用する機会は少ないかもしれないです。
Discussion