Laravelアプリケーション学習⑨（HTTPリクエストの処理）

本記事ではLaravelのアプリケーションを理解し、より良い設計・アーキテクチャを構築できるように学習したことを簡潔にまとめています。

#目次
1.Laravelのアーキテクチャ
2.アプリケーションのアーキテクチャ
3.<font color="LimeGreen">HTTPリクエストとレスポンス</font>
4.データベース
5.認証と許可
6.イベントとキューによる処理の分離
7.コンソールアプリケーション
8.テスト
9.エラーハンドリングとログの活用
10.テスト駆動開発の実践

#3．HTTPリクエストとレスポンス

#3.1　リクエストハンドリング
webブラウザなどを通してユーザーから送信された値（HTTPリクエスト）をLaravelで取り扱う方法を説明する。

##　リクエストの取得
ユーザーからのリクエストは、public/index.php内でIlluminate\Http\Requestクラスのインスタンスとして取得できる。
このインスタンスはHTTPカーネルのhandleメソッドを通してビジネスロジック内で利用できる。

```php
$response = tap($kernel->handle(
   $request = Request::capture()
))->send();
```
Illuminate\Http\RequestクラスはSymfony\COmponent\HttpFoundation\Requestクラスを継承しており、そのインスタンスには、下記変数からの取得情報が含まれる。

・\$_GET
・\$_POST
・\$_COOKIE
・\$_FILES
・\$_SERVICE

これらのリクエストをコントローラで参照するには主に３つの方法がある。
1.<font color="red">Requestファサードの使用</font>
2.<font color="red">Requestクラスのインスタンスをコンストラクタ・メソッドインジェクションを介して使用</font>（Requestオブジェクト)
3.<font color="red">フォームリクエストの使用</font>

## 1. Requestファサード
下記にRequestファサードを使ったリクエストの取得

```php
use Illuminate\Support\Facades\Request;

// "name" キーでリクエストから値を取得する
$name = Request::get('name');
// "name"キーがない場合、[guest]を返す
$name = Request::get('name', 'guest');
```
Requestファサードの実装は、Illuminate\Support\Facades\Requestであり、Requestファサードが呼ばれると、getFacadeAccessorの戻り値に設定した文字列「request」をサービスコンテナに介して解決し、結果的にはIlluminate\Http\Requestクラスのインスタンスへのアクセスとなる。
下記にIlluminate\Support\Facades\Requestを示す。

```php
<?php
namespace Illuminate\Support\Facades;

class Request extends Facade
{
   protected static function getFacadeAccessor()
   {
      return 'request';
   }
}
```
ファサードの仕組みは以前まとめていのでここでは省略。
次に、Requestファサードを使ってリクエストを取得する例をいくつか紹介する。

###取得したリクエストを連想配列に保存する
フォームからの送信されたリクエストを配列で一括取得する場合、***all()***メソッドを使う。

```php
// すべての入力値を$inputsで取得
$inputs = Request::all();
```

###指定した入力項目のみ取得する
引数で指定した入力項目のみを取得する場合、***only()***メソッドを使う

```php
//　配列で指定した入力値のみ、値を取り出す
$inputs = Request::only(['name', 'age']);
$name = $inputs['name'];
```


###アップロードしたファイルを取得する
アップロードされたファイルを取得する場合、***file***メソッドを使う。返却されるオブジェクトは、SpiFileInfoを継承したSymfony\Component\HttpFoundation\File\UploadedFileクラスのインスタンスである。

```php
//　アップロードされたファイルを取得し、$contentに読み込む
$file = Request::file('material');
$content = file_get_contents($file->getRealPath());
```

###クッキーやヘッダ情報の取得
クッキーやヘッダ情報、サーバ情報や実行時の環境情報を取得する例。

```php
// クッキーの値を取得
$name = Request::cookie('name');
// ヘッダ値を取得
$acceptLangs = Request::header('Accept-Language');
// 全サーバ値を取得
$serverInfo = Request::server();
```

## 2. Requestオブジェクト
Illuminate\Http\Requestクラスのインスタンスを直接使用する場合、コンストラクタ・メソッドインジェクションを利用する。
インスタンスの生成はサービスコンテナが担う。

下記に簡単な例を示す。

```php
<?php
declare(strict_types=1);

namespace App\Http\Controllers;

use Illuminate\Http\Request;  //Requestクラスをインポート

class UserController extends Controller
{
   public function register(Request $request)
   {
      //インスタんに対して値を問い合わせ
      $name = $request->get('name');
      $name = $request->get('age');
   }
}
```

### JSONリクエストを扱う
Laravelでは、JSONリクエストを扱うこともできる。
方法は以下の２通り。
１．Content-Type:application/jsonまたは、+jsonが指定されている場合、通常のリクエストと同様***getメソッド***
２．Content-Typeが指定されていなくても、JSONリクエストを扱う場合、***jsonメソッド***

下記にJSONリクエストを取得する例を示す。

```php
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Illuminate\Http\Request;

final class PayloadAction
{
   public function __invoke(Request $request)
   {
      // $result_getと$result_jsonに同じ値が入る。
      $result_get = $request->get('nested');
      $result_json = $reqest->json('nested');
   }
}
```

## ３．フォームリクエスト
フォームリクエストはIlluminate\Http\Requestを継承したクラスで、入力値の取得に加えてバリデーションルールや認証機能などを定義できる機能である。
フォームリクエストはサービスコンテナの***resolvingメソッド***と***afterResolvingメソッド***を使って、インスタンス生成とバリデーション処理が行われている。
下記にフォームリクエストを登録しているサービスプロバイダ（Illuminate\Foundaation\Providers\formRequestServiceProviderクラスを示す。

```php
public function boot()
{
   // ②
   $this->app->afterResoliving(ValidationWhenResolved::class, function ($resolved) {
      $resolved->validateResolved();
   });

   // ①
   $this->app->resolving(FormRequest::class, function ($request, $app) {
      $request = FormRequest::createForm($app['request'], $request);
      
      $request->getContainer($app)->setRedirector($app->make(Redirector::class));
   });
}
```
***＜コード解説＞***
①resolveメソッドは、第一引数で指定したクラスを継承したクラスのインスタンスが生成された直後に、第２引数のクロージャが実行される。
②resolveメソッド実行後、afterResolvingメソッドでは第一引数で指定したインターフェースを実装したクラスのインスタンス生成後、第２引数のクロージャが実行される。

上記のコードを見ると、afterResolvingメソッドでIllumiate\Contracts\Validation\ValidationWhenResolvedインターフェースを実装したクラスのインスタンスが生成されたタイミングで、validateResolvedメソッドがコールされ、validateResolvedメソッド内部で次回紹介するvaildation(rulesメソッドの戻り値である配列）が利用される。