Laravel 6.x : Validator::make() で Validator を生成し、エラーメッセージのプレースホルダを customAttributes で置き換える

  • (ある事情で) FormRequest をメソッド・インジェクションで使用せず、Controller 内で Validator::make() してバリデータを生成し、ちょっと変わった使い方をした
  • その際、バリデーションエラーメッセージ の プレースホルダattributes() が反映されず、少しハマったので、メモ

ドキュメント

sample

FormRequest

final class HogeRequest extends FormRequest implements HogeRequestInterface
{
    public function authorize(): bool
    {
        return true;
    }

    public function attributes(): array
    {
        return [
            'dog' => '犬',
            'cat' => '猫',
        ];
    }

    public function rules(): array
    {
        return [
            'dog' => [
                'required',
            ],
            'cat' => [
                'required',
            ],
        ];
    }

    public function messages(): array
    {
        return [
            'dog.required' => ':attribute は必須です。', //犬 は必須です
            'cat.required' => ':attribute は必須です。', //猫 は必須です
        ];
    }
}
final class HogeController extends Controller
{
    // 通常は FormRequest をメソッド・インジェクションして利用
    public function __invoke(HogeRequest $request)
    {
        // FormRequest をメソッド・インジェクションすると、
        // バリデーションエラーがあった際に、
        // 自動的に元の画面にリダイレクトしてエラーメッセージが表示される
    }
}

Controller (これだと attributes() が効かない)

final class HogeController extends Controller
{
    public function __invoke(Request $request)
    {
        // バリデーション対象のパラメータ
        $postParam = $request->all();

        // HogeRequest インスタンス作成
        $curRequest = new HogeRequest();

        // Validator インスタンス作成
        $validator = Validator::make(
            $postParam,
            $curRequest->rules() //HogeRequest インスタンスからバリデーションルールを取得
        );

        // 特定のリクエストパラメータがバリデーションエラーの場合のみ、元の画面(入力値は保持)にバリデーションエラーメッセージを表示
        if ($validator->errors()->has('cat')) {
            $errors = new MessageBag();
            $errors->add(
                'cat',
                $validator->errors()->get('cat')[0]
            );
            return redirect()->route('xxxx')
                ->withErrors($errors) //エラーメッセージが「cat は必須です」のまま
                ->withInput();
        }

        // 〜 略 〜
    }
}

Controller (こうすると attributes() が効く)

final class HogeController extends Controller
{
    public function __invoke(Request $request)
    {
        $postParam = $request->all();

        $curRequest = new HogeRequest();

        $validator = Validator::make(
            $postParam,
            $curRequest->rules(),
            $curRequest->messages(), //★ココ (HogeRequest インスタンスからエラーメッセージを取得)
            $curRequest->attributes() //★ココ (HogeRequest インスタンスから attributes を取得)
        );

        if ($validator->errors()->has('cat')) {
            $errors = new MessageBag();
            $errors->add(
                'cat',
                $validator->errors()->get('cat')[0]
            );
            return redirect()->route('xxxx')
                ->withErrors($errors) //エラーメッセージが「猫 は必須です」になった!
                ->withInput();
        }

        // 〜 略 〜
    }
}

調査メモ

  • vendor/laravel/framework/src/Illuminate/Validation/Factory.php を見れば make メソッドの引数がわかった。
    /**
     * Create a new Validator instance.
     *
     * @param  array  $data
     * @param  array  $rules
     * @param  array  $messages
     * @param  array  $customAttributes
     * @return \Illuminate\Validation\Validator
     */
    public function make(array $data, array $rules, array $messages = [], array $customAttributes = [])
    {
        $validator = $this->resolve(
            $data, $rules, $messages, $customAttributes
        );

        // The presence verifier is responsible for checking the unique and exists data
        // for the validator. It is behind an interface so that multiple versions of
        // it may be written besides database. We'll inject it into the validator.
        if (! is_null($this->verifier)) {
            $validator->setPresenceVerifier($this->verifier);
        }

        // Next we'll set the IoC container instance of the validator, which is used to
        // resolve out class based validator extensions. If it is not set then these
        // types of extensions will not be possible on these validation instances.
        if (! is_null($this->container)) {
            $validator->setContainer($this->container);
        }

        $this->addExtensions($validator);

        return $validator;
    }