RitoLabo

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

  • 公開:
  • 更新:
  • カテゴリ: PHP Laravel
  • タグ: PHP,Laravel,5.5,5.6,5.7,ServiceContainer,DI,Repository

※2019/2/18 内容を大幅に変更しました。

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.7/5.6/5.5にて動作確認済みです。

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

依存性注入

まずは何も考えずにシンプルなMVCを記述したとして、以下のようなコードがあったとします。

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

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

Sampleクラスに対してRightクラスとLeftクラスをインスタンス化していますが、 このSampleクラスは、RightクラスとLeftクラスがあってこそ全ての処理が実装できる事を意味しています。 つまり「SampleクラスはRightクラスとLeftクラスに依存している」という状態になります。

簡潔に表現すると依存性注入(DI = Dependency Injection)とは、 メインのオブジェクトが依存するオブジェクトを自身の中で具象化するのではなく 抽象化を行いそれらを外から入れてあげる事で、オブジェクト同士がより疎の関係となり、保守性の向上につながる。という事になります。

サービスコンテナ

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

今回は定番の計算機能を実装しつつ、シンプルにDIの流れを見ていこうと思います。

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

class SampleController extends Controller
{
protected $Calculation;

public function __construct()
{
$this->Calculation = new Calculation();
}

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

laravel
├─ app
│   ├─
Repositories
│   │   └─
Calculation

インターフェイス

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

laravel
├─ app
│   ├─ Repositories
│   │   └─ Calculation
│   │       └─
CalculationRepositoryInterface.php

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

laravel/app/Repositories/Calculation/CalculationRepositoryInterface.php
<?php
namespace App\Repositories\Calculation;

interface CalculationRepositoryInterface
{
public function add($arg1, $arg2); // 加算
public function sub($arg1, $arg2); // 減算
public function mul($arg1, $arg2); // 乗算
public function div($arg1, $arg2); // 除算
}

リポジトリクラス

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

laravel
├─ app
│   ├─ Repositories
│   │   └─ Calculation
│   │       ├─
CalculationRepository.php
│   │       └─ CalculationRepositoryInterface.php

CalculationRepository.php を定義していきます。先程作成したCalculationRepositoryInterfaceを実装します。

laravel/app/Repositories/Calculation/CalculationRepository.php
<?php

namespace App\Repositories\Calculation;


class CalculationRepository implements CalculationRepositoryInterface
{
// 加算
public function add($arg1, $arg2)
{
return $arg1 + $arg2;
}

// 減算
public function sub($arg1, $arg2)
{
return $arg1 - $arg2;
}

// 乗算
public function mul($arg1, $arg2)
{
return $arg1 * $arg2;
}

// 除算
public function div($arg1, $arg2)
{
return $arg1 / $arg2;
}
}

サービスプロパイダ

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

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

依存性注入を行う

これで一連のパターンは実装できました。それでは実際にコントローラに依存性注入を行ってみます。

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

namespace App\Http\Controllers;

use App\Repositories\Calculation\CalculationRepositoryInterface AS Calculation;
use Illuminate\Http\Request;

class SampleController extends Controller
{
private $Calculation;

public function __construct(Calculation $calculation)
{
$this->Calculation = $calculation;
}

public function index()
{
$result = [
'add' => $this->Calculation->add(1, 1),
'sub' => $this->Calculation->sub(2, 1),
'mul' => $this->Calculation->mul(3, 2),
'div' => $this->Calculation->div(4, 2),
];

print_r($result);
// => Array
// (
// [add] => 2
// [sub] => 1
// [mul] => 6
// [div] => 2
// )
}
}

依存性注入が行われ、Calculation機能を使えるようになりました。コンストラクタにてタイプヒンティング付きで渡す事で自動で依存解決されるので、この中でインスタンス化しなくても済んでいる事がわかると思います。

まとめ

以上で作業は完了です。オブジェクト同士の関係をシンプルに保つ事で、拡張性や保守性も向上するので是非試してみてください。