Laravel はじめました [3. 構成の概念]

※前回の記事は以下です

https://qiita.com/soso555/items/94a1d97545599dd18035

# 概要
- 今回は [構成の概念](https://readouble.com/laravel/5.7/ja/lifecycle.html) 編

# リクエストのライフサイクル
## 概論
- リクエストの受け口は ```public/index.php``` のみ
    - Composerが生成したオートローダーの定義をロードする
    - ```bootstrap/app.php``` からアプリケーション／サービスコンテナのインスタンスを取得する

## HTTP/Consoleカーネル
- ```app/Http/Kernel.php```
    - 送信されたリクエストを渡され、処理する。全リクエストフローの中心に位置し動作する
    - ```Illuminate\Foundation\Http\Kernel``` クラスを継承し拡張している
    - エラー処理、ログ設定、アプリケーション動作環境の決定、そのほか実際にリクエストが処理される前に行う必要のあるタスクなどのbootstrappers（起動コード）の配列を定義する
    - HTTPセッションの読み書き、アプリケーションがメンテナンスモードであるかの決定、CSRFトークンの確認などのHTTPミドルウェアのリストも定義する
- サービスプロバイダ
    - ```config/app.php``` ファイルの ```providers``` 配列で設定されている
        - 親クラス ```Illuminate\Foundation\Http\Kernel``` に定義されている ```$bootstrappers``` の中に角サービスプロバイダが登録されている
    - サービスプロバイダはデータベース、キュー、バリデーション、ルーティングなど、フレームワークの様々なコンポーネントの初期起動処理に責任をもつ
    - カーネルの handle メソッド内で実行されている
    - プロバイダーを自作する場合は ```app/Providers``` に作成すること
- リクエストのディスパッチ
    - ルーターはそのリクエストをルートかコントローラにディスパッチし、その時にルートに指定されているミドルウェアも実行する
    - これも、カーネルの handle メソッド内で実行されている

# サービスコンテナ
- DI（依存性注入）の実現方法を説明している？
- それぞれのサービスプロバイダの中で、必要なサービス（＝注入されるクラス？）をコンテナに登録している
- 以下の記事がすごく参考になりました
    - [【Laravel】サービスコンテナとは？２つの強力な武器を持ったインスタンス化マシーン。簡単に解説。](https://qiita.com/minato-naka/items/afa4b930a2afac23261b)

## 結合
- シンプルな結合
    - コンテナ自身がリゾルバ―の引数として渡される（＝第二引数に渡すクラス名を、第一引数に文字列として指定する）
    - 意味としては、```app()->bind('呼び出しキーワード', 'インスタンス化方法');```

        ```php
        $this->app->bind('HelpSpot\API', function ($app) {
            return new HelpSpot\API($app->make('HttpClient'));
        });
        ```
    - 他の結合方法との違いが説明できない。。。
        - make() が呼ばれる度に都度インスタンス生成する（＝シングルトンでない）
- シングルトン結合
    - 一度シングルトン結合が解決されるとそれ以降、この結合が参照されるたび、コンテナは同じオブジェクトインスタンスを返す

        ```php
        $this->app->singleton('hogehoge', function ($app) {
            return new HelpSpot\API($app->make('HttpClient'));
        });
        ```
- インスタンス結合
    - 既に存在するオブジェクトのインスタンスをコンテナに結合する
    - 以降の呼び出しには、登録したインスタンスが返される

        ```php
        $api = new HelpSpot\API(new HttpClient);
        $this->app->instance('foobar', $api);
        ```
- プリミティブ結合
    - プリミティブな値をコンテナに登録できる

        ```php
        $this->app->when('App\Http\Controllers\UserController')
                  ->needs('$variableName')
                  ->give($value);
        ```
    - 具体的な呼び出し方は？
        - コントローラー側でのコンストラクタで型を指定すれば、依存解決機能を使って自動的にインスタンス化して渡してくれる
        - もしくは、```app()->make('hogehoge');``` でインスタンス化する

## インターフェイスと実装の結合
- 呼び出しキーワードにインターフェースを指定して具象クラスをインスタンス化したい場合、省略して書けるよ

## コンテキストによる結合
- ```when()->needs()->give()``` を使えば、同じキーワードでも呼び出し元に沿ったインスタンスを返すよ

## タグ付け
- ```tag()``` を使えば、タグ付けした一連のサービスを ```tagged()``` で取得できるよ

## 結合の拡張
- ```extend()``` でサービスの解決結果を修正できるよ

## 依存解決
- makeメソッド
    - ```$this->app->make('HelpSpot\API');``` で、コンテナに結合されたサービスをインスタンス化するよ
    - $app変数へアクセスできない場所なら、```resolve('HelpSpot\API');``` でいけるよ
    - ```makeWith()``` は具体的にどこで使うの？

- 自動注入
    - 以下のコンストラクタでは、自動的に型解決してインスタンスが注入されるよ
        - コントローラ
        - イベントリスナ
        - キュージョブ
        - ミドルウェア
    - コントローラーのコンストラクタで使用するリポジトリを受け取る、なんかはこの仕組みで受け取ってるよ

## コンテナイベント
- コンテナが解決された __後__ に実行する関数を定義できる
    - コールバック関数と考えてよさそう
- すべてのコンテナが解決された時に実行する
    - ```$this->app->resolving(function ($object, $app) {});```
- 特定のコンテナが解決された時に実行する
    - ```$this->app->resolving(HelpSpot\API::class, function ($api, $app) {});```

## PSR-11
- PSR ってなに？
    - [PHP Standards Recommendations = PHP標準勧告](https://www.ritolab.com/entry/90)
- PSR-11 で定義された依存性注入コンテナの共通インタフェースを使用しているよ
    - だから、use でインターフェースを指定すれば、どこでもサービスコンテナを呼び出せるよ
    - でも、なんでもかんでもコレを使うのは良くないよ。基本的には依存解決を使って指定してね。
- 以下が詳しいよ
    - [【PHP】PSR-11 Container Interface（コンテナインタフェース）
](https://www.ritolab.com/entry/107)


# サービスプロバイダ
- 初期起動処理を行う心臓部
    - サービスコンテナの結合や、イベントリスナ、フィルター、それにルートなどを登録することを指す
- ```config/app.php``` にデフォルトのサービスプロバイダが定義されているよ
    - いっぱいあるけど、ほとんどは遅延ロードだよ
- 実際に手を動かした内容は [このコミット](https://github.com/soso555BoBs/test-laravel/commit/ba0774ffaa89e37eab1b35a1b10dff2fd7595901) を参照

## サービスプロバイダの記述
- ```Illuminate\Support\ServiceProvider``` を継承しましょうね
- register と boot の 2 つのメソッドを持つ
    - register ではサービスコンテナの登録 __のみ__ を行うこと
        - なんで？
            - サービスプロバイダがまだロードしていないサービスを意図せず使ってしまうこともありえるから
            - ルールとして「そうしましょうね」ということ。守らないと意図しないエラーが起こる可能性がありますよ
- 以下コマンドでプロバイダを新規作成できるよ

    ```bash
    $ php artisan make:provider RiakServiceProvider
    ```
- Registerメソッド
    - 一つずつ $app から登録してもいいけど、```$bindings``` と ```$singletons``` プロパティを宣言すれば、それぞれのプロパティの中身を自動で登録してくれるよ
- Bootメソッド
    - 他の全サービスプロバイダが登録し終えてから呼び出されるよ
    - ビューコンポーサの登録やサービスコンテナ使って処理したい場合はここに記述するよ
    - 依存解決もしてくれるから、引数を追加すれば自動でインスタンスを受け取れるよ

## プロバイダの登録
- サービスプロバイダを新規作成したら ```config/app.php``` の ```$providers``` プロパティに追加しましょうね

## 遅延プロバイダ
- サービスコンテナの登録 __だけ__ を目的とするなら、遅延ロードさせた方がパフォーマンスは良いよね
- サービスプロバイダにて、以下を行うと指定した結合は遅延ロードさせれるよ
    1. ```protected $defer = true;``` を宣言
    2. ```provides()``` メソッドを定義して、結合名の配列を返却する

# ファサード
- この方の記事にはいつも助けられてます。ありがとうございます。
    - [【Laravel】ファサードとは？何が便利か？どういう仕組みか？](https://qiita.com/minato-naka/items/095f2a1beec1d09f423e)
- 要は、サービスコンテナに登録したクラスのメソッドを static メソッドとしてアクセスできるようにする仕組み
- PHP の [マジックメソッド](https://www.php.net/manual/ja/language.oop5.magic.php) で実現している
- 実際に手を動かした内容は [このコミット](https://github.com/soso555BoBs/test-laravel/commit/48ac02ed3b8c296ee860d9f5873be29470bb710e) を参照

## いつファサードを使うか
- ファサードを定義しまくって一つのコントローラで呼び出しまくると、そのコントローラの責任が大きくなるから良くないよ、という話
- ファサード 対 依存注入
    - 依存注入は、実行環境などの諸条件でモックを使用できるから便利だよね
    - でも、ファサードで呼び出すメソッドは「実行環境などの諸条件でモックを使用」したものに対しても実行できるから、ファサードも負けて無いよ、ということかな。
- ファサード 対 ヘルパ関数
    - ファサードとヘルパ関数は同じ使い方ができるよ、という話

## ファサードの仕組み
- 冒頭にも書いたけど、```__callStatic()``` マジックメソッドにより実現してますよ
- 以下コマンドでコントローラを作って、ドキュメント通りに実装してみた
    - ```$ php artisan make:controller UserController```
- サービスコンテナに登録したクラスをファサードとして使用するには、
    1. ```Illuminate\Support\Facades\``` にクラスを作る
    2. [1.] のクラスには ```getFacadeAccessor()``` メソッドのみ定義し、サービスコンテナに登録された結合名を返す
    3. ```config/app.php``` の ```aliases``` に [1.] を登録する

## リアルタイムファサード
- 以下コマンドでモデルを作って、ドキュメント通りに実装してみた
    - ```$ php artisan make:model Podcast```
- [ファサードの仕組み]でやったような手順を踏まなくても、特定のクラスをファサードクラスとして使用できるよ、という話
- テスト方法も書いてあるが、少しめんどくさそう。モックを注入する方がわかり良いのではないか。