Fusic Tech Blog

Fusion of Society, IT and Culture

Laravelのリクエスト/バリデーション/レスポンス #Laravel輪読会 Vol.5
2021/09/22

Laravelのリクエスト/バリデーション/レスポンス #Laravel輪読会 Vol.5

こんにちは、チーム LIGHT のサラです。

最近週5で自宅で筋トレして、フォームローラーで筋膜リリースをするようにしてから、体の調子がよくなった気がします。

さて、LIGHT の Laravel 輪読会記事も 5 本目になりました。過去の記事は以下からどうぞ。

今回のテーマはHTTPリクエストとレスポンスです。

チームLIGHTの紹介

チームLIGHTは現在エンジニア5名、デザイナー2名、インターン1名の合計8人のチームです。 チームLIGHTの雑記ブログもありますので、よかったらそちらもそうぞ! こちらは技術的な内容ではなく、メンバーの日常のことをつらつらと書いています。

LIGHTブログ

また、不定期でLIGHT@NOONという配信イベントも開催しています。

LIGHT Youtubeチャンネル

Laravel輪読会の紹介

チームLIGHTでは、週に1回1時間、Laravel輪読会を実施しています。

輪読会で読む本はこちらの本です!

PHPフレームワークLaravel Webアプリケーション開発 バージョン8.x対応

PHPフレームワークLaravel Webアプリケーション開発 バージョン8.x対応

2021/6/1に発売されたばかりの本で、Laravel8にも対応しています。

ボリュームもたっぷりな上に、Laravelの設計やテストコードに関する内容も充実しているので、オススメです!

本題

今回読んだページはP.120 ~ P.157 の「4-1 リクエストハンドリング」「4-2 バリデーション」「4-3 レスポンス」についてです。

リクエストハンドリング

4-1-1 リクエストの取得

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

4-1-2 Requestファサード

Requestファサードを使ってリクエストを取得するのもできます。

use Illuminate\Support\Facades\Request;

// "name"キーでリクエストから値を取得する
$name = Request::get('name');
// "name"がない場合、[guest]を返す
$name = Request::get('name', 'guest');
// クッキーの値を取得
$name = Request::cookie('name');
// ヘッダ値を取得
$acceptLangs = Request::header('Accept-Language');
// 全サーバ値を取得
$serverInfo = Request::server();

Requestファサードが呼ばれると、ファサードの実装により結果的にはIlluminate\Http\Requestクラスのインスタンスへのアクセスとなります。

4-1-3 Requestオブジェクト

Illuminate\Http\Requestクラスのインスタンスを直接利用する場合は、Laravelのサービスコンテナ機能を使って、コンストラクタまたはメソッドの引数に型宣言することで取得できます。例えば、routes/web.phpに下のように定義すると、/user/registerにリクエストを送信したら、UserControllerクラスのregisterメソッドを呼びます。

Route::post('/user/register', 'App\Http\Controllers\UserController@register');

UserControllerのregisterメソッドでは、引数からIlluminate\Http\Requestクラスのインスタンスを取得し、処理を実行します。

<?php

declare(strict_types=1);

namespace App\Http\Controllers;

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

class UserController extends UserController
{
    // 引数でRequestクラスのインスタンスを渡す
    pubic function register(Request $request)
    {
        // インスタンスに対して値を問い合わせ
        $name = $request->get('name');
        $age = $request->get('age');
    }
}

4-1-4 フォームリクエスト

フォームリクエストはIlluminate\Http\Requestクラスを継承したクラスで、入力値の取得に加えてバリデーションルールや認証機能などを定義できる機能です。

下記のコマンドを実行すると、フォームリクエストのクラスファイル(UserRegistPost)が生成されます。

$ php artisan make:request UserRegistPost

生成されたフォームリクエストのクラスファイルにはauthorizeメソッドとrulesメソッドが用意されています。authorizeメソッドにはリクエストに対する権限を、rulesにはバリデーションルールを設定できます。

そして、メソッドの引数に型宣言をすると、フォームリクエストを使って入力値を取得できます。

<?php

declare(strict_types=1);

namespace App\Http\Controllers;

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

class UserController extends UserController
{
    // 引数でUserRegistPostクラスのインスタンスを渡す
    pubic function register(UserRegistPost $request)
    {
        // インスタンスに対して値を問い合わせ
        $name = $request->get('name');
        $age = $request->get('age');
    }
}

チームで話したこと

  • 考えてみると Request ファサードってあまり使ったことがない(ミドルウェアになにか挟むときに使ったような気はする)
  • FormRequest の authorize() はマジで使わない、いつも脳死で return true; に変更する儀式をしている

バリデーション

利用者から送信されたデータは、その入力値を検証する仕組みが欠かせません。Laravelでは、バリデーションを簡単に実装できる機能が用意されています。

4-2-1 バリデーションルールの指定方法

バリデーションのルール定義は、検査対象とする入力項目のキー名と、ルールを表す文字列の組み合わせを連想配列で指定できます。

[
    // 配列で指定(必須かつメールアドレスであること)
    'email' => ['required', 'email'], 

    // パイプラインで区切る(必須かつ整数値)
    'age' => 'required|integer', 
]

4-2-2 バリデーションルール

バリデーションルールは豊富な種類が用意されています。

  1. 値の存在確認を行うルール

    $rules = [
     'name' => 'required', // 必須項目である
    ]
  2. 型やフォーマット確認を行うルール

    $rules = [
     'user_no' => 'numeric', // 数値であることを確認
     'alpha' => 'alpha', // 全て英字であることを確認
     'email' => 'email', // メールアドレス形式であることを確認
     'ip_address' => 'ip', // IPアドレス形式であることを確認
    ]
  3. 桁数や文字数、サイズ確認を行うルール

    $rules = [
     // 4桁の数値
     'bank_pass' => 'digits:4', 
    
     // 3種のいずれかであることを確認
     'signal_color' => 'in:green,red,yellow', 
    ]
  4. 他の対象との比較を行うルール

    $rules = [
     // emailとemail_confirmedが同地であることを確認
     'email' => 'confirmed',
    
     // usersテーブルのemailカラムと、mailフィールドの値を比較して重複しないことを確認
     // idが100のレコードは重複を許可する
     'mail' => 'unique:users,email,100'
    ]
  5. バリデーション処理に対するルール

    $rules = [
     'email' => ['bail', 'required', 'unique:posts', 'email'],
    ]

4-2-3 バリデーションの利用

バリデーション機能の利用法として、下記2つの方法があります。

  1. コントローラでのバリデーション
  2. フォームリクエストを使ったバリデーション

それぞれの違いは書籍に詳細に書いてあるのでお読みください。

4-2-4 バリデーション失敗時の処理

バリデーションに失敗した場合、エラー内容をユーザーに伝え、何が起きているか、また、どう修正すれば良いかを提示する必要があります。Laravelでは、バリデーションチェックの結果はMessageBoxオブジェクトで保持されています。ビューでは常に$errorsの名前でMessageBagインスタンスが用意されていて、保持しているセッション中に「errors」キーがあれば、そちらが利用されます。

4-2-5 ルールのカスタマイズ

Validatorクラスのextendメソッドの引数にクロージャを指定し、クロージャ内でルールを定義できます。

// バリデーションルールに「ascii_alpha」を追加
Validator::extend('ascii_alpha', function($attribute, $value, $parameters) {
    // 半角アルファベットならtrueとする
    return preg_match('/^[a-zA-Z]+$/', $value);
});

特定の条件のみバリデーションを追加するには、バリデーションクラスのsometimesメソッドを使用します。

$validator = Validator::make($inputs, $rules);
$validator->sometimes(
    'age',
    'integer|min:18',
    function ($inputs) {
        return $inputs->mailmagazine === 'allow';
    }
);

チームで話したこと

  • バリデーションルール、配列で書くか、文字列で書くか

    • バリデーションルール文字列でかき慣れてるけど、配列の方が見やすい気がしてきた
    • バリデーションルールはなんとなく配列で書いてたけれど、文字列指定と正規表現が衝突するというデメリットは知らなかった

      • だいたいなんでも文字列で渡すのには抵抗がある、どうせ内部でバラされるんだろうし最初からそれっぽいデータ構造で渡せばいいじゃんの気持ち
    • バリデーションルール、同じくおおよそのことで文字列を渡すことに抵抗がある派なので配列で渡す。正規表現のバリデーションは、Cakeでは書いたし、Laravelでも書くこと多そうなので、デメリットがあるなら考えよう
  • sometimes初めてしった。使ってみたい
  • bailはじめてみました

    • bail は便利ですよね、エラーメッセージいっぱい出したくないとき
    • 本(初版)の bail の解説が間違っている公式ドキュメント曰く

    Stop running validation rules for the field after the first validation failure.

    While the bail rule will only stop validating a specific field when it encounters a validation failure, the stopOnFirstFailure method will inform the validator that it should stop validating all attributes once a single validation failure has occurred:

  • ルールのカスタマイズのところ、全部出すためとはいえきたねえ……となった

    • FormRequest 使わない場合でも Validation 行うだけのクラスに分割したほうがよくねという所感
  • Laravel でクエリパラメータのバリデーションするいい方法が未だにわからない

レスポンス

4-3-1 さまざまなレスポンス

アプリケーションからユーザに返却するデータは種類によって返却方法が違います。

文字列返却

そのまま文字列を返却します。デフォルトではContent-Type: text/htmlで返却されるが、text/plainなどに変更するのもできます。

テンプレート出力

Bladeテンプレートなどを出力する場合は、viewヘルパー関数やViewファサードを使ってテンプレートを指定して、そのまま返却することでルーターの処理でレスポンスを生成します。

JSON出力

APIレスポンスに利用されるJSONやクロスドメイン対応のJSONPの場合は、それぞれに対応するメソッドを利用します。

ダウンロードレスポンス

ファイルのダウンロードなどを指示する場合はdownloadメソッドを利用します。

リダイレクトレスポンス

リダイレクト先を指定してリダイレクトを実行します。

Server-Sent Events実装

SSE (Server-Sent Events)を使ってレスポンスを返却するのもできます。書籍に詳細に書いてあるのでお読みください。

4-3-2 リソースクラスを組み合わせた REST API レスポンスパターン

LaravelのAPI ResourceでHATEOASを実装する例が紹介されています。 HATEOASとは?については、書籍をお読みください。

チームで話したこと

  • Resource めんどくさいなって思ってたけど、HATEOASの考え方前提だと大事かもなーとなった - 一方で公開 API を作成しているわけではないので、フロントエンド実装者との兼ね合いで HATEOAS の考え方を採用しないことはありうる
  • HATEOAS なるほどー とりあえず DB からぶっこ抜いて返せばあとはフロントがなんとかしてくれるでしょうぐらいの実装しかしたことがない……

今回の内容に限らず、書籍全体の内容は初心者にはやや難しめですが、チームで話しながら読み進めると、学びが多いです。

次回はミドルウェアなどの予定です。

それではまた次回!

sarah

sarah

Company : Fusic CO., LTD