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を待つか別の妥協案を施行するしかない。