【準備編】Laravelのサービスコンテナを理解しよう|なぜサービスコンテナが有効か.

Laravelのサービスコンテナを初心者が正しく理解することは非常に難易度が高いかと思います。 まずは、サービスコンテナを理解する前にイメージしておくこと考えて見たいと思います。

サービスコンテナを理解する前に、サービスとは何でしょうか。

明確な定義はありませんが、特定の機能/処理を持ったclassのこと。と定義して問題ないかと思います。

サービスはなぜ必要なのでしょうか。

極論、サービスを作らなくてもサービスを作っていくことは可能かと思います。簡単なサービスならば特に考える必要もないでしょう。ただ一定規模のサービスを作ってくと、Fat Controllerの問題にぶち当たります。 Controllerで多くの処理を記述するため、処理が理解しづらい、大きなControllerになってしまいます。

そのために、サービスという一定の規模処理郡をControllerから切り離して、必要に応じて呼び出すことで、シンプルなContolloerになります。また、各処理を疎結合にすることも可能です。

①:実際にサービスを作ってみる

app>Services>CookService.php

サービスを作るディレクトリは特段決まっている訳ではありません。が、app配下にServicesディレクトリを作り、その配下にサービスを定義していくことが一般的です。

今回は、CookServiceを作ってみます。 Cookに wash, cut, bakeといった機能を持ちます。$sozaiを引数に受け取り、$sozaiを処理していきます。

<?php
namespace App\Services;
class CookService
{

    public function wash($sozai)
    {
        $washed_sozai = // washの処理
        return $washed_sozai;
    }


    public function cut($sozai)
    {
        $cuted_sozai = // cutの処理        
        return $cuted_sozai;
    }


    public function bake($sozai)
    {
        $baked_sozai = // bakeの処理        
        return $baked_sozai;
    }
}

②:サービスを使ってみる

上記で作ったサービスを使ってみます。

サービスを作る、サービスを使う、という過程においては必ずしもサービスコンテナを利用する必要はありません。

まずは、サービスコンテナを利用しない方法からいきましょう。

app>Http>Controllers>CurryController.php

CurryControllerで、CookServiceを利用していきましょう。

下記のように、サービスをnewでインスタンス化して、メソッドを呼び出しております。

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\CookService;

class CurryController extends Controller
{

    public function 洗う()
    {
        $cook = new CookService;
        洗済みのじゃがいも = $cook->wash(じゃがいも);

        洗済みのにんじん = $cook->wash(にんじん);

        洗済みのたまねぎ = $cook->wash(たまねぎ);
    }

    public function 切る()
    {
        $cook = new CookService;
        切済みのじゃがいも = $cook->cut(じゃがいも);

        切済みのにんじん = $cook->cut(にんじん);

        切済みのたまねぎ = $cook->cut(たまねぎ);
    }

    public function 焼く()
    {
        $cook = new CookService;
        焼いた野菜 = $cook->bake(じゃがいも, にんじん, たまねぎ);
    }


}

③:依存性の自動解決

各メソッドで$cooksを受け取っています。 newでインスタンス化はしていません。 コンストラクタでタイプヒントした際に、依存性のあるクラスに対して自動的に必要なクラスをnewしてくてれいます。

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\CookService;

class CurryController extends Controller
{


    protected $cooks;

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


    public function 洗う()
    {
        洗済みのじゃがいも = $this->cooks->wash(じゃがいも);

        洗済みのにんじん = $this->cooks->wash(にんじん);

        洗済みのたまねぎ = $this->cooks->wash(たまねぎ);
    }

    public function 切る()
    {
        切済みのじゃがいも = $this->cooks->cut(じゃがいも);

        切済みのにんじん = $this->cooks->cut(にんじん);

        切済みのたまねぎ = $this->cooks->cut(たまねぎ);
    }

    public function 焼く()
    {
        焼いた野菜 = $this->cooks->bake(じゃがいも, にんじん, たまねぎ);
    }


}

④:複数のServicesを利用したい場合

複数のServiceをControllerで利用したい場合はどうしたらいいでしょうか。

1つの方法として make でサービスを呼び出します。

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\CookService;
use App\Services\BuyService; // 追加
use App\Services\WashDishesService; // 追加

class CurryController extends Controller
{

    protected $cooks;
    protected $buys;
    protected $washes;

    public function __construct()
    {

        $this->cooks = app()->make(CookService::class);
        $this->buys = app()->make(BuyService::class);
        $this->washes = app()->make(WashDishesService::class);


    }

    public function 買う()
    {
        $this->buys->goToSupermarket('にんじん');
        $this->buys->goToSupermarket('じゃがいも');
        $this->buys->goToSupermarket('たまねぎ');
    }




    public function 洗う()
    {
        洗済みのじゃがいも = $this->cooks->wash(じゃがいも);

        洗済みのにんじん = $this->cooks->wash(にんじん);

        洗済みのたまねぎ = $this->cooks->wash(たまねぎ);
    }

    public function 切る()
    {
        切済みのじゃがいも = $this->cooks->cut(じゃがいも);

        切済みのにんじん = $this->cooks->cut(にんじん);

        切済みのたまねぎ = $this->cooks->cut(たまねぎ);
    }

    public function 焼く()
    {
        焼いた野菜 = $this->cooks->bake(じゃがいも, にんじん, たまねぎ);
    }

    public function お皿洗い()
    {
        $this->washes->washClear('お皿');
    }


}