RitoLabo

Laravelのサービスコンテナで依存性注入(DI)を行う

  • 公開:
  • カテゴリ: PHP Laravel
  • タグ: PHP,Laravel,5.5,5.4,5.3,5.6,ServiceContainer,DI,Repository

PHPで「DI」とか「DIコンテナ」とかよく聞くと思います。DIとはDependency Injection、つまり依存性注入の事を指しますが、依存関係にあるクラスをそのクラスではなく外から設定してあげる事で、よりスムーズなソース管理や処理の流れを実現します。

今回はLaravelのサービスコンテナを使って、依存性注入を行います。

アジェンダ
  1. 開発環境
  2. 依存性注入
  3. サービスコンテナ
  4. インターフェイス
  5. リポジトリクラス
  6. サービスプロパイダ
  7. 依存性注入を行う

開発環境

今回の開発環境は以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • PHP 7.2/7.1
  • Laravel

Laravelのバージョンは5.6/5.5/5.4/5.3にて動作確認済みです。

Laravelのルートディレクトリを「laravel/」とします。

依存性注入

依存性注入(DI = Dependency Injection)についてですが、「依存」と言うくらいなので、あるオブジェクトを使う場合に別のオブジェクトが必要である関係を指しますが、例えばテストをスムーズに通そうと思った際に、ここが結構ネックになったりします。

簡単な例で言うと、以下のような感じです。

class SampleController extends Controller
{
protected $right;
protected $left;

public function __construct()
{
$this->right = new Right();
$this->left = new Left();
}

Sampleクラスに対して、RightクラスとLeftクラスをインスタンス化していますが、上記例ではこのコントローラは、RightクラスとLeftクラスがあってこそ全ての処理が実装できる事を意味しています。つまり、Sampleクラスが他に依存があるという事です。

簡単な話、依存性注入とは、メインのオブジェクトが、それ自体が依存するオブジェクトをそのまま扱うのではなく、それはそれとして、オブジェクト自体を外から入れてあげる事で、メインのオブジェクトをより「疎」にでき、同時にテスタビリティの向上にもなる。という事になります。

サービスコンテナ

それでは、サービスコンテナを使ってLaravelにて依存性注入を行っていきます。

今回は、わかりやすくカルピスソーダに例えてみようかなと思い、カルピスソーダオブジェクトに対して、カルピス(原液)オブジェクトとソーダオブジェクトとの依存性注入を行っていきます。リポジトリパターンで進めていきます。

冒頭の例に例えれば、DI前は以下のような関係性です。

class CalpisSodaController extends Controller
{
protected $calpis;
protected $soda;

public function __construct()
{
$this->calpis = new Calpis();
$this->soda = new Soda();
}

まずはlaravel/app 配下に Repositories ディレクトリを作成し、その配下に Calpisディレクトリを作成しておきます。

laravel
├─ app
│   ├─
Repositories
│   │   └─
Calpis

インターフェイス

インターフェイスを作成します。laravel/app/Repositories/Calpis 配下に CalpisRepositoryInterface.php を作成します。

laravel
├─ app
│   ├─ Repositories
│   │   └─ Calpis
│   │       └─
CalpisRepositoryInterface.php

インターフェイスを以下のように実装します。

laravel/app/Repositories/Calpis/CalpisRepositoryInterface.php
<?php
namespace App\Repositories\Calpis;

interface CalpisRepositoryInterface
{
// {n}mlのカルピス原液を返却する
public function getCalpis($ml);
}

単純にインターフェイスとしての実装をこれまで通りに行ってください。

リポジトリクラス

続いて、リポジトリクラスを作成します。laravel/app/Repositories/Calpis 配下に CalpisRepository.php を作成します。

laravel
├─ app
│   ├─ Repositories
│   │   └─ Calpis
│   │       ├─
CalpisRepository.php
│   │       └─ CalpisRepositoryInterface.php

CalpisRepository.php を実装していきます。

laravel/app/Repositories/Calpis/CalpisRepository.php
<?php
namespace App\Repositories\Calpis;

use App\Models\Calpis;

class CalpisRepository implements CalpisRepositoryInterface
{
protected $calpis;

public function __construct(Calpis $caplis)
{
$this->calpis = $calpis;
}

public function getCalpis($ml)
{
return $this->calpis->getCalpis($ml);
}
}

これもインターフェイスにそってメソッドを実装しています。定義しているモデル的なクラスから引っ張ってもいいし(既にここで別の依存性注入しちゃっているのはご愛嬌)、普通にメソッドを書いてもOK。

サービスプロパイダ

最後に、サービスプロパイダにこれらを登録します。

laravel/app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(
\App\Repositories\Calpis\CalpsiRepositoryInterface::class,
\App\Repositories\
Calpis\CalpisRepository::class
);
}

依存性注入を行う

これで一連のパターンは実装できました。それでは実際に依存性注入を行ってみます。(ソーダの方も同じ要領でリポジトリパターンを実装しておきます)

laravel/app/Http/Controllers/CalpisSodaController.php
<?php

namespace App\Http\Controllers;

use App\Repositories\Calpis\CalpisRepositoryInterface AS Calpis;
use App\Repositories\Soda\SodaRepositoryInterface AS Soda;

class RoseWineController extends Controller
{
protected $calpis;
protected $soda;

public function __construct(Calpis $Calpis, Soda $Soda)
{
$this->calpis = $Calpis;
$this->soda = $Soda;
}

public function cookCalpisSoda()
{
$calpis = $this->calpis->getCalpis(50);
$soda = $this->soda->getsoda(100);
$calpis_soda = $this->mix($calpis, $soda);

return $calpis_soda;
}

これで依存性の注入が外部から行われました。カルピス1に対してソーダ2、、ちょっと濃いかな。あとで氷を追加する部分も追加しよう。

まとめ

以上で作業は完了です。今回は依存性注入をメインとしてその実装の流れを取り上げましたが、実際にはリポジトリパターン以外でも実装が出来るので、是非試してみてください。