2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LaravelでDIを使う

Last updated at Posted at 2020-06-06

目次

Laravelの記事一覧は下記
PHPフレームワークLaravelの使い方

Laravelバージョン

動作確認はLaravel Framework 7.19.1で行っています

前提条件

eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

Laravelで環境(開発環境と本番環境等)ごとに異なる値を定義する
本記事は上記の内容を理解している前提で書かれています

DIって?

簡単に言うと、作成したクラスをnew演算子を使ってインスタンスを作成せずに、クラスに名前を付けて、付けた名前を使ってインスタンスを呼び出す方法です
そして、名前とクラスのマッピングは独立したファイル内で行います
そうすると、名前とクラスのマッピングファイルを変更するだけで帰ってくるインスタンスのクラスを変えることができます
それの何がいいかというと、ユーティリティクラスやデータベースアクセスクラスなどをテスト時だけダミーの張りぼてクラスに差し替えてテストを実行できるのです

サービスクラス作成

(1) /sample/app/Servicesフォルダ作成
(2) /sample/app/Services/Interfacesフォルダ作成
(3) /sample/app/Services/InterfacesフォルダにSampleService.php作成

SampleService.php
<?php
namespace App\Services\Interfaces;

interface SampleService
{
    public function __construct($sample1 = 0, $sample2 = 0);

    public function getSample1();
    public function setSample1($sample1);

    public function getSample2();
    public function setSample2($sample1);

}

(4) /sample/app/Services/Implフォルダ作成
(5) /sample/app/Services/ImplフォルダにSampleServiceImpl.php作成

App\Services\Impl\SampleServiceImpl.php
<?php
namespace App\Services\Impl;

use App\Services\Interfaces\SampleService;

class SampleServiceImpl implements SampleService
{
    private $sample1;
    private $sample2;

    public function __construct($sample1 = 0, $sample2 = 0)
    {
        $this->setSample1($sample1);
        $this->setSample2($sample2);
    }

    public function getSample1()
    {
        $this->sample1++;
        return $this->sample1;
    }

    public function setSample1($sample1)
    {
        $this->sample1 = $sample1;
    }

    public function getSample2()
    {
        $this->sample2++;
        $this->sample2++;
        return $this->sample2;
    }

    public function setSample2($sample2)
    {
        $this->sample2 = $sample2;
    }

}

(6) /sample/tests/Servicesフォルダ作成
(7) /sample/tests/Services/Implフォルダ作成
(8) /sample/tests/Services/ImplフォルダにSampleServiceImpl.php作成

Tests\Services\Impl\SampleServiceImpl.php
<?php
namespace Tests\Services\Impl;

use App\Services\Interfaces\SampleService;

class SampleServiceImpl implements SampleService
{
    public function __construct($sample1 = 0, $sample2 = 0)
    {
    }

    public function getSample1()
    {
        return 1;
    }

    public function setSample1($sample1)
    {
    }

    public function getSample2()
    {
        return 2;
    }

    public function setSample2($sample2)
    {
    }

}

DI登録

(1) /sample/app/Providers/DiServiceProvider.php作成

DiServiceProvider.php
<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\Interfaces\SampleService;

class DiServiceProvider extends ServiceProvider{

    public function register()
    {
        $env = config('app.env');

        // サービスクラスの登録
        $prefix = 'App\\Services\\Impl\\';
        if ($env === 'test') {
            $prefix = 'Tests\\Services\\Impl\\';
        }

        app()->singleton(SampleService::class, $prefix . 'SampleServiceImpl');

    }

    public function boot()
    {
    }

}

config('app.env')はconfig/app.php内に定義された配列のenv要素を取得しています
config('app.env')の値によって登録されるクラスが変わるようにしています
app()->singletonで先ほど作成したサービスクラスをDI登録しています
singletonというのは、クラスのインスタンスが1つしか生成されないようにすることです
要は、あるクラスに対してnew演算子を使うのは1回だけ。new演算子を使って作成されたインスタンスはフレームワーク内の変数に格納され、2回目以降の呼び出し時はその変数からインスタンスを取り出し返すというものになります
ただし、動作確認しているLaravel7.1.1では使い方によってはシングルトンにならない場合があります。それは後述します

(2) /sample/config/app.php修正
上記で作成したサービスプロバイダをLaravelに登録します
/sample/config/app.php内に定義されているproviders配列にApp\Providers\DiServiceProvider::class,を追記する

app.php
‥‥
    'providers' => [
‥‥
        /*
         * Application Service Providers...
         */
        App\Providers\DiServiceProvider::class,
‥‥
    ],
‥‥

ここに記載されたサービスプロバイダ(Illuminate\Support\ServiceProviderをextendsしたクラス)はLaravelが自動的にロードしてくれます

Controllerにメソッド追加

DI登録したクラスを使ってみましょう
(1) /sample/app/Http/Controllers/SampleController.phpにuse文を追記
use App\Services\Interfaces\SampleService;

(2) /sample/app/Http/Controllers/SampleController.phpにdi1メソッド、di2メソッドを追記

SampleController.php
    public function di1()
    {
        $data = [];

        $sampleService = app()->makeWith(SampleService::class, ['sample1' => 1, 'sample2' => 2]);
        $data['sample1_1'] = $sampleService->getSample1();
        $data['sample1_2'] = $sampleService->getSample2();

        $sampleService = app()->makeWith(SampleService::class);
        $data['sample2_1'] = $sampleService->getSample1();
        $data['sample2_2'] = $sampleService->getSample2();

        $sampleService = app()->makeWith(SampleService::class, ['sample1' => 100, 'sample2' => 200]);
        $data['sample3_1'] = $sampleService->getSample1();
        $data['sample3_2'] = $sampleService->getSample2();

        return view('sample.di', $data);
    }

    public function di2(SampleService $sampleService)
    {
        $data = [];

        $sampleService->setSample1(1);
        $sampleService->setSample2(2);
        $data['sample1_1'] = $sampleService->getSample1();
        $data['sample1_2'] = $sampleService->getSample2();

        $sampleService = app()->makeWith(SampleService::class);
        $data['sample2_1'] = $sampleService->getSample1();
        $data['sample2_2'] = $sampleService->getSample2();

        $sampleService = app()->makeWith(SampleService::class, ['sample1' => 100, 'sample2' => 200]);
        $data['sample3_1'] = $sampleService->getSample1();
        $data['sample3_2'] = $sampleService->getSample2();


        return view('sample.di', $data);
    }

上記を見て分かる通り、makeWithを実行してDI登録したクラスを呼び出すこともできますし、
メソッドの引数を型宣言(タイプヒンティング)することでも呼び出すことができます
makeWithでは第二引数を使ってDI登録したクラスのコンストラクタに引数を渡すことができます
先ほどDI登録したサービスプロバイダ作成のところで述べましたが、
動作確認しているLaravel7.1.1では使い方によってはシングルトンにならない場合があります
それはmakeWithの第二引数に渡した値が、phpのempty関数に与えた結果falseの場合です
makeWithの第二引数に['sample1' => xxx, 'sample2' => xxx]のように配列を渡しているとシングルトンにならずに新しいインスタンスが作成され返されます。コンストラクタの引数が与えられているからですね

(3) /sample/routes/web.phpに下記を追記
Route::get('sample/di1', 'SampleController@di1');
Route::get('sample/di2', 'SampleController@di2');

viewの作成

(1) /sample/resources/views/sample/di.blade.phpファイル作成

di.blade.php
<html>
    <head>
        <title>sample</title>
    </head>
    <body>
        <div>{{$sample1_1}}</div>
        <div>{{$sample1_2}}</div>
        <div>{{$sample2_1}}</div>
        <div>{{$sample2_2}}</div>
        <div>{{$sample3_1}}</div>
        <div>{{$sample3_2}}</div>
    </body>
</html>

動作確認

http://localhost/laravelSample/sample/di1

実行結果

2
4
1
2
101
202

Controllerにメソッド追加したところで説明したようにmakeWithに第二引数を与えているとシングルトンにならないことがわかります

http://localhost/laravelSample/sample/di2

実行結果

2
4
3
6
101
202

Controllerにメソッド追加したところで説明したようにmakeWithに第二引数を与えないとシングルトンになっていることがわかります

/sample/.envのAPP_ENV=localをAPP_ENV=testに変えて実行してみましょう

http://localhost/laravelSample/sample/di1

実行結果

1
2
1
2
1
2

http://localhost/laravelSample/sample/di2

実行結果

1
2
1
2
1
2

DI登録されたクラスが変わったことがわかります

/sample/.envのAPP_ENVをlocalに戻しておきましょう

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?