Laravelの基本:DI(Dependency Injection/依存性注入)

laravel
この記事は約9分で読めます。
記事内に広告が含まれます。

LaravelのDI(DependencyInjection:依存性注入)を使ったサンプルプログラムです。

スポンサーリンク

仕様概要

今回作るアプリケーションは占いの結果を表示してくれるものです。
ページを開くとおみくじを引き「大吉~大凶」までの占いしてくれます。

回答のパターン以下の6パターンです

  • 大吉
  • 中吉
  • 小吉
  • 末吉
  • 大凶

この中からランダムで1つを選択して表示します。

通常占いクラスを作成た場合は
プログラム内で
$uranai = new Fortune();
などと記述してFortuneController内でFortuneクラスをインスタンス化していると思います。
しかしこの方法ではFortuneControllerはFortuneクラスに依存していることとなり
Fortuneクラスが変更された場合にはFortuneControllerも変更しなければなりません
そこでDIを利用してFortuneControllerの変更なしにコンポーネントを差し替える方法を紹介します

インターフェースの作成

Laravelのインストールを終わらせてください。
今回はDBは使いません。

まずはインターフェースを作成します。
このインターフェースを継承したものは同じように扱えるようにするため
インターフェースを作成することで機能を抽象化します。

おみくじのインターフェースを作成します、uranai関数を定義し
このインターフェースを実装した関数を後で作成します。

app/Interfaces/FortuneInterface.php

<?php
namespace App\Interfaces;

interface FortuneInterface
{
    public function uranai(): string;
}

コンポーネント(関数の実体)を作成します。

おみくじプログラムの本体です、ここにおみくじの仕組みを作成します。
app\Componentsフォルダを作成します。
app\Components\Fortune.php

<?php
namespace App\Components;

use App\Interfaces\FortuneInterface;

const FORTUNE = [
    0 => '大吉',
    1 => '中吉',
    2 => '小吉',
    3 => '末吉',
    4 => '吉',
    5 => '凶',
    6 => '大凶',
];

class Fortune implements FortuneInterface {
    public function uranai(): String {
                // 0~6の乱数を生成
        $fortune = rand(0, 6);
                return self::FORTUNE[$fortune];
    }
}

サービスを作成します

app\Services\FortuneService.php

<?php
namespace App\Services;

use App\Interfaces\FortuneInterface;

class FortuneService
{
    protected $FortuneComponent;

    public function __construct(FortuneInterface $FortuneComponent){
        $this->FortuneComponent = $FortuneComponent;
    }

    public function uranai(){
        return  $this->FortuneComponent->uranai();
    }
}

コントローラを作成します。

ここがLaravelのコントローラとなり、入り口になります。

app\Http\Controllers\FortuneController.php

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Services\FortuneService;

class FortuneController extends Controller
{
    protected $FortuneService;

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

    public function index(Request $request) {
        echo "<h1>".$this->FortuneService->uranau()."<h1>";
    }
}

サービスプロバイダを作成する。

DI(依存性注入)利用において、注入するインスタンスの生成、また、クラスへの注入も、サービスコンテナが担う。
サービスプロバイダでComponentを注入する。

php artisan make:provider FortuneServiceProvider
個のコマンドでサービスプロバイダが作成される

以下のように修正する
app\Provider\FortuneServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\Interfaces\FortuneInterface;  // 追加

class FortuneServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(FortuneInterface::class, function() {
            return new \App\Components\Fortune();
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

サービスプロバイダをプロバイダのオートロードに登録する

config/app.php

...
    'providers' => [
        ...
        ...
        App\Providers\FortuneServiceProvider::class, // 最後に追記
    ]

ブラウザで動作させるためにRoutingに追記

...
use App\Http\Controllers\FortuneController;
...
...
...
Route::get('/', [FortuneController::class, 'index']);
...

動作確認

サイトを開くと占いの結果が表示されれば完成

url: localhost

大吉

テスト

ここまでコンポーネント作成し、インターフェースを定義し、サービスを作り、コントローラに読み込みと
面倒な作業をしてきましたが、ここからがDIを使うメリットが出てきます。

テストを作成する場合に結果が大吉や大凶など毎回違う値が返ってくると
テストがし辛いので
テスト用にapp\Components\TestFortune.phpコンポーネントを作成します。
このコンポーネントはFortuneInterfaceを継承しuranauメソッドを定義します、このとき必ず大吉が返却されるように細工をします。

app\Components\Fortune.php

<?php
namespace App\Components;

use App\Interfaces\FortuneInterface;

const FORTUNE = [
    0 => '大吉',
    1 => '中吉',
    2 => '小吉',
    3 => '末吉',
    4 => '吉',
    5 => '凶',
    6 => '大凶',
];

class TestFortune implements FortuneInterface {
    public function uranai(): String {
                // 0~6の乱数を生成
        // $fortune = rand(0, 6);
                // 0必ず0の大吉を返す
        $fortune = 0;
                return self::FORTUNE[$fortune];
    }
}

サービスドライバを編集

app\Provider\FortuneServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\Interfaces\FortuneInterface;  // 追加

class FortuneServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(FortuneInterface::class, function($app) {
            if (app()->runningUnitTests()) {
                return new \App\Components\TestFortune();
            }else{
                return new \App\Components\Fortune();
            }
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

これでユニットテストのときだけ「大吉」を返す処理ができました。

コメント

タイトルとURLをコピーしました