Optional Chaining + Laravel FormRequestでバリデメッセージを

表示する

株式会社SCOUTER @kotamat

  • kotamat
  • SCOUTER CTO
  • サーバー、インフラ寄り
  •  🚴‍♂️

だけkotamats

転職にまつわるサービスを展開

エージェント

転職者

求人企業

転職にまつわるサービスを展開

エージェント

転職者

求人企業

面接・履歴書では

わからない転職者の

リファレンスチェック

今までLaravueでやってきた

しゃべる内容

  • FormRequest便利
  • FormRequest辛い
  • Optional Chaining便利
  • Vue template辛い
  • こうやって解決した

FormRequest

class PostFormRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required',
        ];
    }
}

ルール定義して

class FormController
{
    public function __invoke(PostFormRequest $request)
    {
        return $request->validated();
    }
}

Controllerのメソッドインジェクションに入れるだけで

バリデーションしてくれる

便利👌

FormRequest辛い

class PostFormRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name'                       => 'required',
            'user.parent.children.*.name' => 'required',
        ];
    }
}

めっちゃネストしてるルール

ドットつなぎの文字列がキー

普通にハンドリングすると…

      try {
        await this.$axios.$post('/form', payload)
      } catch (err) {
        if (err && err.response && err.response.status === 422) {
          this.err['user.parent.children.0.name'] =
            err.response.data.errors['user.parent.children.0.name']
        }
      }

非常に冗長

Optional Chaining便利

Optional Chaining?

  • TC39のStage 1のproposal
  • Babel7から入ってきた
  • nullがはいるかもしれないネストしたオブジェクトを参照するときに、途中がnullだったらnullで返す
var street = user.address && user.address.street;

var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined
var street = user.address?.street

var fooValue = myForm.querySelector('input[name=foo]')?.value

さっきのは

      try {
        await this.$axios.$post('/form', payload)
      } catch (err) {
        if (err && err.response && err.response.status === 422) {
          this.err['user.parent.children.0.name'] =
            err.response.data.errors['user.parent.children.0.name']
        }
      }

こうできる!

      try {
        await this.$axios.$post('/form', payload)
      } catch (err) {
        if (err?.response?.status === 422) {
          this.err['user.parent.children.0.name'] =
            err?.response?.data?.errors['user.parent.children.0.name']
        }
      }

ちなみに

これを

{
  "staff.0.id": ["The id field is required."],
  "staff.0.name": ["The name field is required."],
  "staff.1.id": ["The id field is required."],
  "staff.1.name": ["The name field is required."],
}

こうしてくれる

{
  "staff": {
    "0": {
      "name": ["The name field is required."],
      "id": ["The id field is required."],
    },
    "1": {
      "name": ["The name field is required."],
      "id": ["The id field is required."],
    }
  }
}

さっきのやつはこうなる

        if (err?.response?.status === 422) {
          this.err = convertValidationError(err?.response?.data?.errors)
        }
        if (err?.response?.status === 422) {
          this.err['user.parent.children.0.name'] =
            err?.response?.data?.errors['user.parent.children.0.name']
        }

良さそう

Vue template辛い

結局やりたいのはこんな感じ

    <div
      v-for="child in err?.user?.parent?.children || []"
      class="error"
    >
      {{ child.name}}
    </div>

しかしエラーが出てしまう

    err?.user?.parent?.children || []

  Raw expression: v-for="child in err?.user?.parent?.children || []"


  1  |
  2  |  <div>
  3  |    <div v-for="child in err?.user?.parent?.children || []" class="error">
     |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  4  |      {{ child.name }}
  5  |    </div>

Vue 2系はtemplateにBabel使ってない

Vue3なら入りそう

現状の回避策

import o from 'optional-value'
 
const someObject = {
  foo: {
    bar: 'hoge'
  }
}
const value = o(someObject, 'foo.bar')
// => hoge
const noneValue = o(someObject, 'foo.boo')
// => null
const obj = o(someObject, 'foo')
// => {bar: 'hoge'}
    <div
      v-for="child in err?.user?.parent?.children || []"
      class="error"
    >
      {{ child.name}}
    </div>
    <div
      v-for="child in o(err, 'user.parent.children') || []"
      class="error"
      :key="child.name"
    >
      {{ child.name }}
    </div>

まとめ

  • LaravelのFormRequestは便利だけどネストしたエラーメッセージはハンドリングが辛い
  • Optional Chainingを使うと、js内ではよしなに展開してくれる。
  • ただし、テンプレートで使いたい場合は、Vue3を待つか別の妥協案を施行するしかない。

SCOUTERではエンジニアを募集しております!

Made with Slides.com