RitoLabo

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

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

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

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

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

開発環境

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

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

Laravelのバージョンは5.8/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前は以下のような関係性です。

<?php

namespace App\Http\Controllers;

use App\Service\CalculationService;

class SampleController extends Controller
{

protected $Calculation;

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

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

サービスクラス

まずは注入する計算用のサービスクラスを作成します。laravel/app 配下に Services ディレクトリを作成し、その配下に CalculationServiceクラスを作成します。

laravel
├─ app
│   ├─
Services
│   │   └─
CalculationService.php
laravel/app/Services/CalculationService.php
<?php

namespace App\Service;


class CalculationService
{
// 加算
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のルートディレクトリへ移動し、以下のartisanコマンドを叩いてサービスプロパイダを生成します。

# Laravelルートディレクトリへ移動
cd /path/to/laravel

# サービスプロパイダ作成
php artisan make:provider CalculationServiceProvider

# 実行結果
[demo@localhost laravel]$ php artisan make:provider CalculationServiceProvider
Provider created successfully.

laravel/app/Providers配下にCalculationServiceProvider.phpが生成されるので、register()メソッドを以下に定義します。

laravel/app/Providers/CalculationServiceProvider.php
<?php

namespace App\Providers;

use App\Service\CalculationService;
use Illuminate\Support\ServiceProvider;

class CalculationServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('Calculation', function($app) {
return new CalculationService();
});
}

public function boot()
{
}
}

最後に、オートロードの対象として、設定ファイルに登録します。

laravel/config/app.php
'providers' => [
/*
* 省略
*/
App\Service\CalculationService::class,

],

依存性注入を行う

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

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

namespace App\Http\Controllers;

use App\Service\CalculationService;
use Illuminate\Http\Request;

class SampleController extends Controller
{
private $Calculation;

public function __construct(CalculationService $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機能を使えるようになりました。コンストラクタにてタイプヒンティング付きで渡す事で自動で依存解決される(コンストラクタインジェクションともいいます)ので、この中でインスタンス化しなくても済んでいる事がわかると思います。

まとめ

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