普段、エラーバッグからエラーを取得する際、$errors->first('name')などとしますが、この時裏ではどんな処理が行われているかご存知でしょうか?私自身、全然意識せずに使っていましたが、調べてみると実はdefaultという名前付きエラーバッグからエラーを取得している事が分かりました。

前回の記事、名前付きエラーバッグによるエラー管理の執筆時にエラーバッグの構造について調べたのでまとめます。久々にUnder the hoodシリーズ(?)です。前回の記事の内容が多く含まれているのでまだの方は先にそちらを読むことをオススメします。

$errorsの構造

どう言うことか?$errorsの構造を紐解きながら説明します。エラー発生時にblade側でdd($errors)とすると以下の様な出力が確認できます。

Illuminate\Support\ViewErrorBag {#1285 ▼
  #bags: array:1 [▼
    "default" => Illuminate\Support\MessageBag {#1286 ▼    // ← key が default
      #messages: array:1 [▼
        "name" => array:1 [▼
          0 => "名前を入力してください。"
        ]
      ]
      #format: ":message"
    }
  ]
}

これを見て分かることは

  • $errorsの実態はViewErrorBagのインスタンスである
  • ViewErrorBag$bagsプロパティにMessageBagdefaultというキーで格納されている
  • エラーメッセージはMessageBag$messagesプロパティに格納されている

つまり、$errors->first('name') とした時は、bagsプロパティの、defaultキーの、(MessageBagの)messagesプロパティの、nameキーの最初のエラーを取得している訳ですね。想像してたより深い構造をしています。

$errors->first(…)の処理を追う

構造からデータへアクセスする際のイメージが掴めたので実際のコードを追ってみましょう。$errors->first(...)とした時、$errorsはViewErrorBagのインスタンスなのだから、ViewErrorBagにfirst()メソッドが定義されているのだろう、と普通は思いますよね。ところが、ViewErrorBagにはそんなメソッドがありません。代わりに以下のマジックメソッドがcallされています。

...
    /**
     * Dynamically call methods on the default bag.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->getBag('default')->$method(...$parameters);
    }
...

マジックメソッド __call() についての詳しい解説はPHPのドキュメントを確認して頂きたいのですが、
簡潔に説明すると、

  • クラスに存在しないメソッドがcallされた場合に実行されるメソッド
  • 第一引数($method)は実行しようとしたメソッド名
  • 第二引数($parameters)は実行しようとしたメソッドに渡す予定だった引数

なので例えば、$errors->first('name') 実行時は$methodfirst、 $parameters[‘name’]となります。getBag()は指定のkeyのMessageBagを$bagsから取得するメソッドです。従って、$errors->first(...)defaultというキーに紐づいた MessageBagクラスのfirst()をcallしているという事ですね。

名前付きエラーバッグの場合

今度は名前付きエラーバッグの場合の処理を追ってみましょう。前回の記事のおさらいになりますが、名前付きエラーバッグからエラーを取得する際は$errors->エラーバッグ名->first(...) などとします。例えば、エラーバッグ名がuserなら、

$errors->user->first('name');

です。

このコードを見ると、userプロパティがあるのかな?と思いがちですが、(Again!)そんなプロパティはViewErrorBagに存在しません。今度は__get()というマジックメソッドがcallされます。

...
    /**
     * Dynamically access a view error bag.
     *
     * @param  string  $key
     * @return \Illuminate\Contracts\Support\MessageBag
     */
    public function __get($key)
    {
        return $this->getBag($key);
    }
...

getBag() は前項でも出てきましたね。ここでは紐づいたMessageBagを返しているだけの様です。

まとめ

通常のエラーバッグと名前付きエラーバッグ、どちらもエラー参照時にはgetBag()にてMessageBagを取得していることが分かりました。違いは、通常のエラーバッグ時はハードコードされたdefaultというキーを使用しているのに対し、名前付きエラーバッグは任意のキーを使用しているという点のみで、処理自体に違いはありません。冒頭で、通常のエラーバッグがdefaultという名前付きエラーバッグである、と言ったのはそういう意味です。なので実は以下の様にエラーを参照することもできます(意味は全く無いですが)

$errors->default->first('name');

By hikaru