[Laravel]フォームリクエストバリデーションについて

## はじめに
ララベルのバリデーション実装時に躓いたので、備忘録も兼てまとめたいと思います。

###  バリデーションの種類
Laravelのバリデーションは大きく分けて二つあります。

1.validateメソッドを使ってコントローラーに記述する

2.フォームリクエストバリデーション

### どちらを使った方がいいのか
時と場合によりますが、特に理由がなければフォームバリデーションを使った方が良い。

理由としてファットコントローラーを避けるため。コントローラーにはなるべくロジックは書きたくないので、分けられるなら分けた方が良い

フォームリクエストバリデーションのファイルはバリデーションの処理しか書かないので分かりやすい。


今回はフォームリクエストバリデーションについてまとめたいと思います
### フォームリクエストバリデーションの使い方

下記の簡単なフォームで実装します。
![image](https://firebasestorage.googleapis.com/v0/b/ciita-c6ea6.appspot.com/o/user%2Fimages%2F24FeDZXCSibIqvoE?alt=media&token=cdabc58b-8b52-4db3-9a32-780828486c37)

まずは下記コマンドでフォームリクエストバリデーションを実装するファイルを作ります。
```
php artisan make:request StoreEventRequest
```

そうすると
App\Http\Requests配下にStoreEventRequestが生成されます

### コントローラー

1.useでStoreEventRequestを参照する

2.保存処理をするメソッドの引数を、StoreEventRequestにする。


```php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreEventRequest;

class EventController extends Controller
{
    // 省略
    public function store(StoreEventRequest $request)
    {
    // 処理
    }
    // 省略
}
```

### フォームリクエストバリデーション

初期表示
```php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreEventRequest
 extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
            //
        ];
    }
}
```
実装後
```php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreEventRequest
 extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
            'event_name' => ['required', 'max:50'],
            'information' => ['required', 'max:200'],
            'event_date' => ['required', 'date'],
            'start_time' => ['required'],
            'end_time' => ['required', 'after:start_time'],
            'max_people' => ['required', 'numeric', 'between:1,20'],
            'is_visible' => ['required', 'boolean']
        ];
    }

    /**
     * 定義済みバリデーションルールのエラーメッセージ取得
     *
     * @return array<string, string>
     */
    public function attributes(): array
    {
        return [
        'event_name' => 'イベント名',
        'information' =>'イベント詳細',
        'event_date' =>'イベント日付',
        'start_time' =>'開始時間',
        'end_time' =>'終了時間',
        'max_people' =>'定員数',
        'is_visible' =>'表示・非表示'
        ];
    }
/**
 * バリデーションエラーのカスタム属性の取得
 *
 * @return array<string, string>
 */
public function messages(): array
{
    return [
        'event_name.required' => ':attributeを入力してください',
        'event_name.max_length' => ':attributeは50文字以内で入力してください',
        'information.required' =>':attributeを入力してください',
        'event_date.required' =>':attributeを入力してください',
        'start_time.required' =>':attributeを入力してください',
        'end_time.required' =>':attributeを入力してください',
        'max_people.required' =>':attributeを入力してください',
        'is_visible.required' =>':attributeを選択してください'
        ];
}
}

```
## 説明
### rules()について
rules()には、キーにフォームのname要素、バリューにどんなバリデーションをつけたいか記述する。
```php
    public function rules()
    {
        return [
            'name要素' => どんなバリデーションにするか,
        ];
    }
```
バリューに関しては色々なレパートリーがある。

### attributes()について
messages()の:attributeの名前を指定することが出来ます。

### messages()について
rules()で指定したエラーが起きたときに、attributes()で指定した名前でエラーを表示することが出来ます。

### bladeでエラーを表示させる方法

フォームの上に下記記述をすることで、エラーをリスト表示することが出来ます
```php
@if ($errors->any())   //エラーがあるか判定
    <div style="color:red" class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li> //エラーを表示
            @endforeach
        </ul>
    </div>
@endif
```
 
 ### 実際の表示
![image](https://firebasestorage.googleapis.com/v0/b/ciita-c6ea6.appspot.com/o/user%2Fimages%2FofLHPwCdGa3cWZ4T?alt=media&token=be5170a6-10c3-4fab-9435-66278449b368)

### 実際のblade
```php
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            イベント新規登録
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
            <div class="max-w-2xl py-4 mx-auto">
            
                @if ($errors->any())
                    <div style="color:red" class="alert alert-danger">
                        <ul>
                            @foreach ($errors->all() as $error)
                                <li>{{ $error }}</li>
                            @endforeach
                        </ul>
                    </div>
                @endif
               
                 <form method="POST" action="{{ route('events.store') }}">
                     @csrf
                       <div>
                           <x-jet-label for="event_name" value="イベント名" />
                           <x-jet-input id="event_name" class="block mt-1 w-full" type="text" name="event_name" :value="old('event_name')"  />
                       </div>
                       <div class="mt-4">
                           <x-jet-label for="information" value="イベント詳細" />

                           <textarea row="3" id="information" name="information" class="block mt-1 w-full">{{ old('information') }}</textarea>
                       </div>

                       <div class="md:flex justify-between">
                       <div class="mt-4">
                           <x-jet-label for="event" value="イベント日付" />
                           <x-jet-input id="event_date" class="block mt-1 w-full" type="text" name="event_date"  />
                       </div>
     
                       <div class="mt-4">
                           <x-jet-label for="start_time" value="開始時間" />
                           <x-jet-input id="start" class="block mt-1 w-full" type="text" name="start_time"  />
                       </div>
                       
                       <div class="mt-4">
                           <x-jet-label for="end_time" value="終了時間" />
                           <x-jet-input id="end_time" class="block mt-1 w-full" type="text" name="end_time"  />
                       </div>
                     </div>
                     <div class="md:flex justify-between items-end">
                       <div class="mt-4">
                          <x-jet-label for="max_people" value="定員数" />
                          <x-jet-input id="max_people" class="block mt-1 w-full" type="number" name="max_people" />
                       </div>
                       <div class="flex space-x-4 justify-around">
                          <input type="radio" name="is_visible" value="1" chacked />表示
                          <input type="radio" name="is_visible" value="0" />非表示
                       </div>
                       <x-jet-button class="ml-4">
                               新規登録
                       </x-jet-button>
                    </div>
                 </form>
               </div>
            </div>
        </div>
    </div>
    <script src="{{ mix('js/flatpickr.js')}}"></script>
</x-app-layout>

```

これでフォームリクエストバリデーションを使って、エラーを表示することが出来ました。
次はリスト表示ではなく、入力欄の上にバリデーションメッセージを表示させたいと思います。

### 実際の表示

すみません。
レイアウト崩れてますが、入力欄の上にバリデーションが表示されています。

![image](https://firebasestorage.googleapis.com/v0/b/ciita-c6ea6.appspot.com/o/user%2Fimages%2FU7tpNDgJ3oEjg6h8?alt=media&token=3adce94a-75c4-4b06-a03b-e4447e430f2c)
### 実装方法

各入力欄の上に下記記述
```
@if($errors->has('event_name'))
    @foreach ($errors->get('event_name') as $message)
        <p style="color: red">{{  $message }}</p>
    @endforeach
@endif
```
$messageは、StoreEventRequestの、messages()の値を表示させています。

### 実際のblade
```php
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            イベント新規登録
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
            <div class="max-w-2xl py-4 mx-auto">
            
                @if ($errors->any())
                    <p style="color: red">入力内容に誤りがあります</p>
                @endif
               
                 <form method="POST" action="{{ route('events.store') }}">
                     @csrf

                        @if($errors->has('event_name'))
                            @foreach ($errors->get('event_name') as $message)
                                <p style="color: red">{{  $message }}</p>
                            @endforeach
                        @endif
                        <div>
                           <x-jet-label for="event_name" value="イベント名" />
                           <x-jet-input id="event_name" class="block mt-1 w-full" type="text" name="event_name" :value="old('event_name')"  />
                        </div>

                        @if($errors->has('information'))
                            @foreach ($errors->get('information') as $message)
                                <p style="color: red">{{  $message }}</p>
                            @endforeach
                        @endif
                        <div class="mt-4">
                           <x-jet-label for="information" value="イベント詳細" />
                           <textarea row="3" id="information" name="information" class="block mt-1 w-full">{{ old('information') }}</textarea>
                        </div>

                        <div class="md:flex justify-between">

                            @if($errors->has('event_date'))
                                @foreach ($errors->get('event_date') as $message)
                                    <p style="color: red">{{  $message }}</p>
                                @endforeach
                            @endif
                            <div class="mt-4">
                                <x-jet-label for="event" value="イベント日付" />
                                <x-jet-input id="event_date" class="block mt-1 w-full" type="text" name="event_date"  />
                            </div>
                        
                            @if($errors->has('start_time'))
                                @foreach ($errors->get('start_time') as $message)
                                    <p style="color: red">{{  $message }}</p>
                                @endforeach
                            @endif
                            <div class="mt-4">
                                <x-jet-label for="start_time" value="開始時間" />
                                <x-jet-input id="start" class="block mt-1 w-full" type="text" name="start_time"  />
                            </div>

                            @if($errors->has('end_time'))
                                @foreach ($errors->get('end_time') as $message)
                                    <p style="color: red">{{  $message }}</p>
                                @endforeach
                            @endif
                            <div class="mt-4">
                               <x-jet-label for="end_time" value="終了時間" />
                               <x-jet-input id="end_time" class="block mt-1 w-full" type="text" name="end_time"  />
                            </div>
                        </div>

                        @if($errors->has('max_people'))
                            @foreach ($errors->get('max_people') as $message)
                                <p style="color: red">{{  $message }}</p>
                            @endforeach
                        @endif
                        <div class="md:flex justify-between items-end">
                        <div class="mt-4">
                           <x-jet-label for="max_people" value="定員数" />
                           <x-jet-input id="max_people" class="block mt-1 w-full" type="number" name="max_people" />
                        </div>

                       <div class="flex space-x-4 justify-around">
                          <input type="radio" name="is_visible" value="1" chacked />表示
                          <input type="radio" name="is_visible" value="0" />非表示
                       </div>
                       <x-jet-button class="ml-4">
                               新規登録
                       </x-jet-button>
                    </div>
                 </form>
               </div>
            </div>
        </div>
    </div>
    <script src="{{ mix('js/flatpickr.js')}}"></script>
</x-app-layout>
```

本日は以上となります。
最後まで見ていただきありがとうございました！