Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

今回やりたいこと

フォームリクエストバリデーションの方法を使って入力のバリデーションは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書き換えはできたが
書き換える方法、場所はこれでいいのか今でも悩んでる。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away