LoginSignup
41

More than 5 years have passed since last update.

LaravelのformバリデートでAjax通信時のエラーを独自のフォーマットに変更する

Posted at

今回やりたいこと

フォームリクエストバリデーションの方法を使って入力のバリデーションはcontrollerから分離しているときに
フォームエラー時のjsonで返す際の構造をオレオレフォーマットに変えて返す。

Laravel+Aureliaでのサイト構築でLaravel側の実装はjsonで返答するAPIのみとしたとき
エラー時に返すjsonはAurelia側でハンドリングすることを考えるとフォーマットを統一したほうがよいので、
エラー時のフォーマットを決め、404もシステムエラーもフォーム入力エラーも全てその形で出力したかった。

さらにLaravelではajax時などjsonで返す仕組みがすでにあるため、
その実装をそのまま生かす方向で実装したい。

いきなり方法から。

ソースを読んだみた感じだと、Requestクラスのresponseをオーバーライドすれば返りのjsonを変更できそう。
バリデーションしてるクラスにresponseを追加。

UserStoreRequest.php
class UserStoreRequest extends Request
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'name' => 'required|between:1,100',
            'email' => 'required|email|unique:users,email|max:255',
            'password' => 'required|between:8,100',
        ];
    }

# responseを上書きする
# ajax,jsonで返答時以外は親に流す

    public function response(array $errors)
    {
        if ($this->ajax() || $this->wantsJson()) {
            # ここでいじれる
            return new JsonResponse(['message' => 'じゆうじざい!','errors'=>$errors], 422);
        }

        parent::response($errors);
    }

}

方法が分かったところでフォーマットについて考える。

ここでフォーマットをいじれるからと言って直書きしてちゃ意味がない。
フォーマットを決め、そのフォーマットを扱うクラスを用意する。

実装したいフォーマットを決める

エラー時のjsonフォーマットはどうするのがよいか以下を参考に考えた。
WebAPIでエラーをどう表現すべき?15のサービスを調査してみた

悩んだ結果以下のフォーマットにしてみた。
フォームエラー時はerrorsにエラー内容を個別に入れて、
例外発生時はerrorsはnullにしてcodeとmessageをそのまま突っ込む形。
errorsにfieldとか入れようかと悩んだがfieldごとにぶん回したほうが楽かと思い
まずはそのまま入れてみる。

{
    "code":エラーコード,
    "message":"エラーメッセージ",
    "errors":{
        "フィールド名":[
            "エラーの説明"
        ],
    }
}

エラー時のjsonを扱うクラスを作る

今回はエラー時のレスポンス統一するのが目的なので内容を扱うクラスを用意する。
プロパティをそのままjsonにして返す感じ。
これをコンテナに突っ込み、シングルトンで使えるようにする。

jsonResponseError.php
# ※gettersetterは省いてあります。

class JsonResponseError implements JsonResponseErrorInterface
{
    private $code;

    private $message;

    private $errors;
    /**
     * 全プロパティを配列化する
     *
     * @return array
     */
    public function toArray ()
    {
        return get_object_vars($this);
    }

    /**
     * 配列化したあとにjson encodeした値を返す
     *
     * @return string
     */
    public function toJson()
    {
        return json_encode($this->toArray());
    }

    /**
     * Jsonでレスポンスするオブジェクトを返す
     *
     * @return JsonResponse
     */
    public function response()
    {
        return new JsonResponse($this->toArray(), $this->code);
    }

    /**
     * JsonResponseErrorをjsonの文字列にして返す
     *
     * @return string
     */
    public function __toString()
    {
        return $this->toJson();
    }
}

実装してみる

先ほどのUserStoreRequest.phpを作ったクラスを使う形に書き換える。

UserStoreRequest.php
    public function response(array $errors)
    {
        if ($this->ajax() || $this->wantsJson()) {
            # ここでいじれる
            return Error::create('入力項目にエラーがあるから確認してね。', 422, $errors)->response();
        }

        parent::response($errors);
    }

実装前のjson

まずは現状の状態、formへajaxでpostすると以下のフォーマットで帰ってくる。
field名:エラーメッセージの配列というところ。

{
    "name":[
        "The name field is required."
    ],
    "email":[
        "The email field is required."
    ],
    "password":[
        "The password field is required."
    ]
}

実装後のjson

これが、こう。

{
    "code":422,
    "message":"入力項目にエラーがあるから確認してね。",
    "errors":{
        "name":[
            "The name field is required."
        ],
        "email":[
            "The email field is required."
        ],
        "password":[
            "The password field is required."
        ]
    }
}

できあがり。

これでいいのか

結果として上記の方法でフォームエラー時のjson書き換えはできたが
書き換える方法、場所はこれでいいのか今でも悩んでる。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41