Optional Chaining + Laravel FormRequestでバリデメッセージを
表示する
株式会社SCOUTER @kotamat
誰
- kotamat
- SCOUTER CTO
- サーバー、インフラ寄り
- 🚴♂️
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5156289/kotamat.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5157899/twitter.png)
だけkotamats
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5788368/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5788370/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/6398775/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467691/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467694/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467708/pasted-from-clipboard.png)
転職にまつわるサービスを展開
エージェント
転職者
求人企業
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467708/pasted-from-clipboard.png)
転職にまつわるサービスを展開
エージェント
転職者
求人企業
面接・履歴書では
わからない転職者の
リファレンスチェック
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467691/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467694/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467708/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467712/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467713/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467712/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467713/pasted-from-clipboard.png)
今までLaravueでやってきた
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467712/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5467713/pasted-from-clipboard.png)
しゃべる内容
- 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のメソッドインジェクションに入れるだけで
バリデーションしてくれる
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/6150795/Postman.png)
便利👌
FormRequest辛い
class PostFormRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required',
'user.parent.children.*.name' => 'required',
];
}
}
めっちゃネストしてるルール
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/6151044/Postman.png)
ドットつなぎの文字列がキー
普通にハンドリングすると…
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使ってない
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/6163196/pasted-from-clipboard.png)
Vue3なら入りそう
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/6163198/pasted-from-clipboard.png)
現状の回避策
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ではエンジニアを募集しております!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5156339/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5157711/LaravelLogo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5157712/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3134363636332f33306233343131332d623734352d343965662d646536372d6531393164663138343463392e706e67.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5157713/aws_logo_smile_1200x630.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5157715/Vue.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5157728/php_logo.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5473386/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/888603/images/5473390/pasted-from-clipboard.png)