Laravel勉強 その6 リクエストのライフサイクル

## 前回記事
[Laravel勉強 その5 Laravelのデプロイ](https://qiita.com/masa_skd30/items/c805e7f457c138a5e8eb)

## 目的
- 勉強のため、Laravel 7.xの公式ドキュメントを読み解いていく。
- 今回は、公式ドキュメントの「リクエストのライフサイクル」の章を扱う。

## 参考
[Laravel 7.x リクエストのライフサイクル](https://readouble.com/laravel/7.x/ja/lifecycle.html)


## イントロダクション
>このドキュメントの目的は、どのようにLaravelが動作しているのか、ハイレベルな概念を理解してもらうためです。
>フレームワークの概念をより良く知ってもらうことで、すべてに対し「不可解さ」が減り、より自信を持ってアプリケーションを開発してもらえます。
>これらの言葉を今すぐ理解できなくても、気落ちしないでください。ただ何が行われているかを理解するための基本だけを理解してください。
>ドキュメントの他のセクションで明確にされる内容を理解すれば、知識は育っていくことでしょう。

この辺は確かに気になっていたので楽しみ。

## 概論
### 始まり
>アプリケーションに対するリクエストは、すべてpublic/index.phpファイルが入り口になります。
>Webサーバ(Apache/Nginx)の設定により、すべてのリクエストをこのファイルへ渡しています。
>index.phpファイルは、さほどコードを持っていません。フレームワークの残りをロードするための入り口にすぎません。

>index.phpファイルは、Composerが生成したオートローダーの定義をロードします。
>それから、bootstrap/app.phpスクリプトから、Laravelアプリケーションのインスタンスを取得します。
>Laravel自身の最初のアクションは、アプリケーション／サービスコンテナのインスタンスを生成することです。

オートローダーとは、「ファイルを自動で読み込む仕組みのこと」らしい。
composerを利用してライブラリをインストールした際に作成される vendor/autoload.php がオートローダーの本体で、
このファイルだけを一度だけrequireしてあげれば、vendor以下のライブラリをすべてrequireしてくれるとのこと。
逆にこれがなければ、phpファイルの冒頭には大量のrequireが必要になってしまうと。

## HTTP/Consoleカーネル
>次にアプリケーション起動のきっかけにより、送信されてきたリクエストをHTTPカーネルかコンソールカーネルのどちらかに送ります。
>これらのカーネルは、全リクエストフローの中心に位置し動作します。
>ここではapp/Http/Kernel.phpにあるHTTPカーネルへ焦点を合わせます。

ちなみにコンソールカーネルは、Laravelで使用するコマンド（php artisan ＊＊＊）のセットアップをしてくれるらしい。

>HTTPカーネルはIlluminate\Foundation\Http\Kernelクラスを拡張しています。
>このKernelクラスは、リクエストの実行前に処理されるbootstrappers（起動コード）の配列を定義してます。
>これらの起動コードはエラー処理、ログ設定、アプリケーション動作環境の決定、そのほか実際にリクエストが処理される前に行う必要のあるタスクです。

>HTTPカーネルはさらに、アプリケーションによりリクエストが処理される前に通す必要のある、HTTPミドルウェアのリストも定義しています。
>これらのミドルウェアはHTTPセッションの読み書き、アプリケーションがメンテナンスモードであるかの決定、CSRFトークンの確認などを行います。

>HTTPカーネルのhandleメソッドの使い方はとてもシンプルで、Requestを受け取り、Responseをリターンします。
>カーネルをアプリケーション全体を表す大きなブラックボックスだと考えてください。HTTPリクエストを流し込み、HTTPレスポンスが返ってきます。

要は、リクエストが処理される前に以下の処理を行うと。
  - 起動コード（エラー処理、ログ設定、動作環境設定など）の定義
  - ミドルウェアの定義（セッションの読み書き、メンテナンスモード、CSRFトークンの確認）の定義

これらの前処理が終わったらリクエストを受け取り、色々な処理をしてレスポンスを返すと。

### サービスプロバイダ
>カーネル初期起動時の重要な処理の一つは、アプリケーションのサービスプロバイダをロードすることです。
>アプリケーションの全サービスプロバイダは、config/app.phpファイルのproviders配列で設定されています。
>最初に全プロバイダのregisterメソッドが呼び出され、その後に登録されている全プロバイダのbootメソッドが呼び出されます。

サービスプロバイダーは、もの（オブジェクト）をサービスコンテナにバインド（配置）し、サービスコンテナからものを解決（取得）する役目で、
サービスコンテナは、アプリケーションのブートストラッププロセスで開始されたすべてのものが配置されるkey => valueの場所とのこと。

>サービスプロバイダはデータベース、キュー、バリデーション、ルーティングなど、フレームワークのさまざまなコンポーネントの初期起動処理に責任をもちます。
>フレームワークにより提供されている全機能を初期化し、設定するものですから、サービスプロバイダはLaravelの初期処理の過程全体で一番重要な機能です。

なるほど。

### リクエストのディスパッチ
>アプリケーションの初期処理が済み、全サービスプロバイダが登録されたら、ディスパッチ（実行制御の移行）するためにルーターへRequestが手渡されます。
>ルーターはそのリクエストをルートかコントローラにディスパッチし、その時にルートへ指定されているミドルウェアも実行されます。

ディスパッチが何なのかはあまりわからないが、とりあえず準備が終わったら実行に移る、ということだと思う。
調べてみたけれどもあまりわからず…

## サービスプロバイダの精査
>サービスプロバイダはLaravelアプリケーションの初期起動段階における、まさに鍵となるものです。
>アプリケーションインスタンスが作成され、サービスプロバイダが登録され、初期起動を終えたアプリケーションでリクエストが処理されます。とてもシンプルです！

>Laravelアプリケーションがどう構築されているか、そしてサービスプロバイダによる初期起動の仕組みをしっかりと把握することは、とても重要です。
>皆さんのアプリケーションのためのデフォルトサービスプロバイダは、app/Providersディレクトリに設置されています。

>AppServiceProviderはデフォルトで完全に空っぽです。
>このプロバイダは皆さんのアプリケーション自身の初期起動や、サービスコンテナの結合を追加するために用意されています。
>大きなアプリケーションであれば、まとまった初期起動種別ごとに分け、好きなだけサービスプロバイダを作成できます。

サービスプロバイダーの作成については、以下リンク先がわかりやすかった。
[Laravelのサービスプロバイダーの仕組みやメリットとは](https://www.geekfeed.co.jp/geekblog/laravel-service-providers)
[Laravel サービスプロバイダーついに理解](https://reffect.co.jp/laravel/laravel-service-provider-understand)
[サービスプロバイダーについて](https://laraweb.net/practice/2051/)
試しに作ってみようと思う。

作成前のapp/Providersはこんな感じ。

``` 
# ll app/Providers/
合計 20
-rw-rw-r-- 1 root root  578  5月 21 20:33 AuthServiceProvider.php
-rw-rw-r-- 1 root root  403  5月 21 20:33 AppServiceProvider.php
-rw-rw-r-- 1 root root  380  5月 21 20:33 BroadcastServiceProvider.php
-rw-rw-r-- 1 root root  710  5月 21 20:33 EventServiceProvider.php
-rw-rw-r-- 1 root root 1658  5月 21 20:33 RouteServiceProvider.php
``` 

リンク先の例に従って、サービスプロバイダーを作成する。
毎回、Dockerのコンテナ内に入って、というのを繰り返すのは面倒なので、コンテナを指定してコマンドを実行するようにする。

``` 
# docker exec -it e1394b974a68 php artisan make:provider AnimalServiceProvider /コンテナIDを指定
Provider created successfully.
``` 

成功したっぽい。
app/Providersを確認してみる。

``` 
# ll app/Providers/
合計 24
-rw-r--r-- 1 root root  374  6月 24 16:58 AnimalServiceProvider.php
-rw-rw-r-- 1 root root  403  5月 21 20:33 AppServiceProvider.php
-rw-rw-r-- 1 root root  578  5月 21 20:33 AuthServiceProvider.php
-rw-rw-r-- 1 root root  380  5月 21 20:33 BroadcastServiceProvider.php
-rw-rw-r-- 1 root root  710  5月 21 20:33 EventServiceProvider.php
-rw-rw-r-- 1 root root 1658  5月 21 20:33 RouteServiceProvider.php
``` 

ちゃんと作成されている。
中身はこんな感じ。

```:app/Providers/AnimalServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AnimalServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

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

このファイルを、以下の様に編集する。

```:app/Providers/AnimalServiceProvider.php 
<?php
 
namespace App\Providers;
 
use Illuminate\Support\ServiceProvider;
use App\Animals\Bird;
use App\Animals\Food;
 
class AnimalServiceProvider extends ServiceProvider
{
    /**
     * Register the Bird class instance to the container.
     * Food class will be auto injected
     */
    public function register() {
        $this->app->bind('bird', function($app){
    		return new Bird($app->make(Food::class));
    	});
    }
}
``` 

ポイントとしては（たぶん）こんなところ？
- use
  関連する（必要な）クラスを登録しておく。
  この例のサービスプロバイダーは
    - アプリケーションがBirdクラスを使用できる様に、サービスコンテナに登録する
    - BirdクラスはFoodクラスを必要とする
    
  ため、Birdクラス・Foodクラスを登録する。

- register
  bindメソッドを使用して、Birdクラスを'bird'というキーでサービスコンテナにバインドする。
  
- boot
  デフォルトではbootメソッドがあるが、これはregisterメソッドによる初期処理が完了した後に実行されるもの。
  使用しないため削除する。

完成したサービスプロバイダーを、config/app.php に登録する。

```:config/app.php 
（略）
/*
 * Application Service Providers...
 */
App\Providers\AppServiceProvider::class,
App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\AnimalServiceProvider::class,        /ここを追加
（略）
``` 

Birdクラス、Foodクラスを作成する。

``` 
# mkdir app/Animals

# vi app/Animals/Bird.php
# vi app/Animals/Food.php
```

```
# cat app/Animals/Bird.php
<?php
namespace App\Animals;
use App\Animals\Food;

class Bird
{
    private $food = null;
    public function __construct(Food $food){
        $this->food = $food;
    }

    public function sound(){
        return "tweet tweet";
    }

    public function getFood(){
        return $this->food;
    }
}

# cat app/Animals/Food.php
<?php
namespace App\Animals;

class Food
{
    public function foodType(){
        return "warms";
    }
}

?>
``` 

コントローラーを作成する。

``` 
# docker exec -it e1394b974a68 php artisan make:controller AnimalController
Controller created successfully.
``` 

成功したっぽい。
app/Providersを確認してみる。

``` 
# ll app/Http/Controllers/
合計 8
-rw-r--r-- 1 root root 123  6月 29 15:36 AnimalController.php
-rw-rw-r-- 1 root root 361  5月 21 20:33 Controller.php
``` 

作成されている。
中身はこんな感じ。

```:app/Http/Controllers/AnimalController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class AnimalController extends Controller
{
    //
}
``` 

このファイルを、以下の様に編集する。

```:app/Http/Controllers/AnimalController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class AnimalController extends Controller
{
    public function index ()
    {
        $bird = app()->make('bird');
        $returnText = "Sound : ". $bird->sound();
        $returnText .= "<br>Food : ". $bird->getFood()->foodType();
        echo $returnText;
    }
}
``` 

routes/web.phpの末尾に以下を追加

```:./routes/web.php 
Route::get('/animal', 'AnimalController@index');
``` 

これで　http://\<IP\>/animal　にリクエストすると、
Sound : tweet tweet
Food : warms
が表示された。

わかったようなわからないような感じだが、とりあえずちゃんと動いたので今回はここまで。