ServiceProviderを理解するためだけに作ったプロジェクト
サービスプロバイダの解説を読んでもなかなか仕組みや使うことのメリットが分からなかったので、
理解するためだけのプロジェクトを作ってみた。
画面に環境構築すると初期画面にクラス名とcry()の実行結果を表示している。
この実装を追うことでサービスプロバイダの使い方と仕組みを理解する。
Animalのインターフェイスとその中にcry()を定義
<?php
namespace App\Animal;
interface AnimalInterface {
public function cry();
}
コンストラクターでAnimalInterfaceを受けてexecute()でcry()を実行して$soundをviewで表示
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use \App\Animal\AnimalInterface;
class AnimalActionController extends Controller
{
public $animal;
public function __construct(AnimalInterface $animal)
{
$this->animal = $animal;
}
public function execute()
{
$sound = $this->animal->cry();
return view('animal.index', compact('sound'));
}
}
ソースは下記
<p>
{{$sound}}
</p>
「App\Services\Cat: にゃん」 を表示するためのAnimalInterfaceを受けている実装クラスは下記
<?php
namespace App\Services;
use App\Animal\AnimalInterface;
class Cat implements AnimalInterface
{
public function cry()
{
return get_class($this) . ': にゃん';
}
}
Controllers/AnimalActionControllerにはどこにもcatクラスのインスタンスを利用する記述がない。
にも関わらず、何故利用できるかと言うと
ServiceContainerにバインドするために使われるものがServiceProvider
サービスコンテナ の説明は公式を参照
Laravelのサービスコンテナは、クラス間の依存を管理する強力な管理ツールです。依存注入というおかしな言葉は主に「コンストラクターか、ある場合にはセッターメソッドを利用し、あるクラスをそれらに依存しているクラスへ外部から注入する」という意味で使われます。
AnimalActionControllerのコンストラクターで依存注入(AnimalInterface)を行っている。
依存解決のためバインドしてるのは下記
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AnimalServiceProvider extends ServiceProvider
{
/**
/**
* Register the Bird class instance to the container.
* Food class will be auto injected
*/
public function register() {
app()->bind('App\Animal\AnimalInterface', 'App\Services\Cat');
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
resister()に登録している。
第一引数のインターフェイスが呼ばれた場合に第二引数のクラスを呼び出してインスタンスを生成。
よってAnimalActionControllerにわたる$this->animalはCatクラスとなる。
$this->animal->cry()でCatクラスのcry()が呼び出せる。
※定義したサービスプロバイダは/config/app.phpの'providers'に登録することが必要
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
Illuminate\Broadcasting\BroadcastServiceProvider::class,
Illuminate\Bus\BusServiceProvider::class,
Illuminate\Cache\CacheServiceProvider::class,
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
Illuminate\Cookie\CookieServiceProvider::class,
Illuminate\Database\DatabaseServiceProvider::class,
Illuminate\Encryption\EncryptionServiceProvider::class,
Illuminate\Filesystem\FilesystemServiceProvider::class,
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
Illuminate\Hashing\HashServiceProvider::class,
Illuminate\Mail\MailServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
App\Providers\AnimalServiceProvider::class, //一番下に追加した
依存関係の管理をServiceProviderで行うことで容易にインスタンスの差し替えを行うことが可能
Catクラスに変わるDogクラスを用意する
<?php
namespace App\Services;
use App\Animal\AnimalInterface;
class Dog implements AnimalInterface
{
public function cry()
{
return get_class($this) . ': Wooooooo!!!!!!!';
}
}
このDogクラスをAnimalActionControllerで使うためにはAnimalServiceProviderでbindするクラスを差し替えるだけでよい
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AnimalServiceProvider extends ServiceProvider
{
/**
/**
* Register the Bird class instance to the container.
* Food class will be auto injected
*/
public function register() {
app()->bind('App\Animal\AnimalInterface', 'App\Services\Dog'); //bindの第二引数をCat → Dogに変えるだけ
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
すると「Cat → Dog」の変更だけで、
Controllerのコンンストラクターで受けるインスタンスを差し替えすることが可能。
cry()はdogクラスに定義された内容となる。
ここで特筆すべきことはControllerのソースは全く変えることなく、インターフェイスを受けるインスタンスを差替することができたこと。
インターフェイスに依存して、その解決をServiceProviderに任せることでControllerは特定のクラスを意識することがなり、疎結合な状態を保つことが可能になる。
最後に
git cloneして動かしてみると理解が深まるかもしれないです。
この記事でサービスプロバイダの理解がなんとなく深まったら
ドキュメントや他の詳細な解説を読むとさらに理解が深まると思います。
https://readouble.com/laravel/6.x/ja/providers.html