【PHP/Laravel】Formファサードで入力フォーム作成&バリデーション設定&日本語化

# はじめに
Webサイトやシステムを作る際、何かしらの入力フォームを用意するケースは多々あると思います。
そして、そのフォームで入力された情報が不適切な場合、入力者にその旨を伝える必要があります。

本記事では、それらを実現するためのフォーム作成およびバリデーションの設定方法をまとめます。

### 本記事で行うこと
- Formファサードを活用した入力フォームの作成
- バリデーションの設定
- バリデーションエラーメッセージの日本語化
- バリデーションエラーメッセージを日本語にして画面表示

# 開発環境・前提

### 開発環境
- OS：Windows11 22H2
- PHP 8.2.4
- Laravel 9.44.0

### 前提
- ローカルにLaravel開発環境があり、開発サーバーを起動済みであること。
- 各ファイル・ルート・メソッド名は任意。
- デザインは特に考慮せず、機能・動作の確認のみ実施。
- DBへのデータ保存は行わず、機能・動作の確認のみ実施。

# 手順
##### 1\. 入力フォーム作成で「Formファサード」を用いるため、以下のコマンドで`Laravel Collective`パッケージをインストール。
```md:コマンドライン
composer require laravelcollective/html
```
（参考：Laravel Collectiveの公式サイトおよびGitHub）

https://laravelcollective.com/docs/6.x/html

https://github.com/LaravelCollective/html

##### 2\. 以下のコマンドで、`app/Http/Requests/`にリクエストァイルを作成（今回の例では`CreateMessageRequest.php`）。
```md:コマンドライン
php artisan make:request CreateMessageRequest
```

##### 3\. `routes`ディレクトリの`web.php`でルーティングを編集。
```web.php
Route::get('message', [App\Http\Controllers\MessageController::class, 'index']);
Route::post('message', [App\Http\Controllers\MessageController::class, 'post']->name('postMessage');
```

##### 4\. 作成したリクエストファイルを編集。
```diff_php:CreateMessageRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

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

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, mixed>
     */
    public function rules()
    {
        return [
+           'message' => 'required',
        ];
    }
}
```

##### 5\. `app/Http/Controllers/`にコントローラーを作成・編集（今回の例では`MessageController.php`）。

```MessageController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Controller as BaseController;
use App\Http\Requests\CreateMessageRequest;

class MessageController extends BaseController
{
    public function index()
    {
        return view('message');
    }

    public function post(CreateMessageRequest $request)
    {
        $input = $request->validated();
        return view('message')->with('inputMessage', $input);
    }
}
```

##### 6\. `resources/views/`にbladeファイルを作成（今回の例では`message.blade.php`）& bodyタグ内に以下を追記。

```mesage.blade.php
<!-- 該当部分を抜粋 -->
<body>
    @if ($errors->any())
        <div>
            <ul>
                @foreach ($errors->all() as $error)
                    <li>{{ $error }}</li>
                @endforeach
            </ul>
        </div>
    @endif

    {{ Form::open(['route' => 'postMessage']) }}

        {{ Form::label('message', 'タイトル：') }}
        {{ Form::text('message', null) }}

        {{ Form::submit('送信') }}
        
    {{ Form::close() }}

    @if (isset($inputMessage))
        <p>送信したメッセージ：{{ $inputMessage['message'] }}</p>
    @endif
</body>
```

また、ブラウザで確認しやすいよう背景色を調整するために、styleタグ内に以下を追記。
```diff_php:mesage.blade.php
<!-- 該当部分を抜粋 -->
<style>
    body {
        font-family: 'Nunito', sans-serif;
+       background-color: #eee;
    }
</style>
```

##### 7\. 以下のコマンドを順番に入力し、`resources/lang/ja/`に`validation.php`を作成。
```md:コマンドライン
php -r "copy('https://readouble.com/laravel/8.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
php -f install-ja-lang.php
php -r "unlink('install-ja-lang.php');"
```
（参考：Laravel公式サイト）

https://readouble.com/laravel/9.x/ja/validation-php.html

##### 8\. `config`ディレクトリの`app.php`にて、以下の部分を編集。
```diff_php:app.php
// 該当部分を抜粋

/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
- 'timezone' => 'UTC',
+ 'timezone' => 'Asia/Tokyo',

/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/

- 'locale' => 'en',
+ 'locale' => 'ja',
```

##### 9\. 作成した`validation.php`の「カスタムバリデーション属性名」の欄にて、日本語化用に以下の通り追記。
```diff_php:validation.php
/*
|--------------------------------------------------------------------------
| カスタムバリデーション属性名
|--------------------------------------------------------------------------
|
| 以下の言語行は、例えば"email"の代わりに「メールアドレス」のように、
| 読み手にフレンドリーな表現でプレースホルダーを置き換えるために指定する
| 言語行です。これはメッセージをよりきれいに表示するために役に立ちます。
|
*/

'attributes' => [
+   'message' => 'メッセージ',
],
```

##### 10\. `http://localhost/message`に接続し、ブラウザで確認。
　　フォームに何も入力せずに「送信」を押下すると、次の画像の通りエラーメッセージが表示。
![タイトル入力フォーム_requiredバリデーション_日本語化2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512880/729bb7c2-247f-0b06-c149-af87d21ce701.png)
# ポイントの解説
##### Formファサードの導入（[手順1](#1-入力フォーム作成でformファサードを用いるため以下のコマンドでlaravel-collectiveパッケージをインストール)）
Formファサードを活用すれば、入力フォーム作成を簡潔に行うことができます。
記述量の差はあまりないかもしれませんが、いちいちタグで閉じたり、POST・PUT・DELETE処理時のCSRF対策用トークン設置の記述をせずに済みます。
入力数の多いフォームだと、より効果を発揮すると思います。
```message.blade.php
<!-- 該当部分を抜粋 -->
<!-- Formファサードを使わない場合 -->
<form method="POST" action="{{ route('postMessage') }}" accept-charset="UTF-8">
    @csrf

    <label for="message">メッセージ：</label>
    <input name="message" type="text" id="message">

    <input type="submit" value="送信">
</form>

<!-- Formファサードを使う場合 -->
{{ Form::open(['route' => 'postMessage']) }}

    {{ Form::label('message', 'メッセージ：') }}
    {{ Form::text('message', null) }}

    {{ Form::submit('送信') }}
    
{{ Form::close() }}
```

##### ルーティング（[手順3](#3-routesディレクトリのwebphpでルーティングを編集)）
`->name('postMessage')`で名前付きルートにし、bladeファイルでルートを指定しやすくしています。

##### リクエスト（[手順4](#4-作成したリクエストファイルを編集)）
- authorizeメソッドでfalseが返却されると403エラーになるので、trueに変更しています。
これで常にリクエストが認可され、コントローラーに記載のメソッドが実行されます。
- rulesメソッドでバリデーションを定義します。
今回はmessageに対して`required`のみ適用しています。

##### コントローラー（[手順5](#5-apphttpcontrollersにコントローラーを作成編集今回の例ではmessagecontrollerphp)）
- `use App\Http\Requests\CreateMessageRequest;`の記述で、前の手順で作成した`CreateMessageRequest`が使用可能になります。
- indexメソッドで画面表示を実行しています。
- postメソッドでテキスト送信および画面表示を実行しています。
①`$input = $request->validated()`では、`CreateMessageRequest`で定義したバリデーションを通過した入力値が変数`$input`に格納されます。
②withメソッドで変数`$input`をビューに渡しています。

##### ビュー（blade）（[手順6](#6-resourcesviewsにbladeファイルを作成今回の例ではmessagebladephp-bodyタグ内に以下を追記)）
- `$erros->any()`では、エラーがある場合はtrueが返却されるので、`@if ($errors->any()) ～ @endif`までの内容はエラー時のみ表示されます。
- `@foreach ～ @endforeach`で、全てのエラーメッセージを1つずつ取り出してリスト表示しています。
今回はバリデーションを1つだけ定義しているので、表示されるエラーメッセージも1つです。
- `@if (isset($inputMessage)) ～ @endif`の部分で、入力内容が正常に送信されたら、その内容を画面に表示するようにしています。

##### バリデーションエラーメッセージの日本語化（[手順7～10](#7-以下のコマンドを順番に入力しresourceslangjaにvalidationphpを作成)）
バリデーションエラーメッセージ用ファイルを作成の上、日本語化の設定を行っています。
今回はmessage入力欄だけを用意したので、エラーメッセージの日本語化もmessageのみです。

なお、次のように`CreateMessageRequest.php`に他のバリデーションも加え、それに該当する誤った入力をすると、以下の画像のように表示されます。

```CreateMessageRequest.php
// 該当部分を抜粋
public function rules()
{
    return [
        'message' => 'required|integer|max:10',
    ];
}
```
![タイトル入力フォーム_requiredバリデーション_日本語化3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512880/2486bc97-e05e-2c78-cc75-bc568058aa40.png)
messageに対して`required`の他に`interger`と`max:10`を追記することで、「入力値必須」かつ「整数で10以下の数字」のみを受け付ける状態になります。

試しに数字の「3」を入力・送信すると、正常に処理されます。

(数値は半角)
![タイトル入力フォーム_requiredバリデーション_日本語化4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512880/6a428a6a-d09e-45fa-a02c-01bf364788b7.png)
`required`・`interger`・`max`に関するバリデーションエラーメッセージは、デフォルトで`resources/lang/ja/validation.php`に以下のように記述されているため、そのまま日本語化されます。
なお、今回のattributeは「message」であり、[手順9](#9-作成したvalidationphpのカスタムバリデーション属性名の欄にて日本語化用に以下の通り追記)で「メッセージ」と日本語に変換しています。

```validation.php
// 該当部分をそれぞれ抜粋
'integer'              => ':attributeは整数で指定してください。',

'max'                  => [
    'numeric' => ':attributeには、:max以下の数字を指定してください。',
    'file'    => ':attributeには、:max kB以下のファイルを指定してください。',
    'string'  => ':attributeは、:max文字以下で指定してください。',
    'array'   => ':attributeは:max個以下指定してください。',
],

'required'             => ':attributeは必ず指定してください。',
```
また、他にも`between`・`boolean`・`string`などもよく使います。
```validation.php
// 該当部分をそれぞれ抜粋
'between'              => [
    'numeric' => ':attributeは、:minから:maxの間で指定してください。',
    'file'    => ':attributeは、:min kBから、:max kBの間で指定してください。',
    'string'  => ':attributeは、:min文字から、:max文字の間で指定してください。',
    'array'   => ':attributeは、:min個から:max個の間で指定してください。',
],

'boolean'              => ':attributeは、trueかfalseを指定してください。',

'string'               => ':attributeは文字列を指定してください。',
```

（例1）`CreateMessageRequest.php`で`between`を指定した場合（`integer`と組み合わせて`numeric`扱いにし、整数1～9を指定）
```diff_php:CreateMessageRequest.php
// 該当部分を抜粋
public function rules()
{
    return [
-        'message' => 'required|integer|max:10',
+        'message' => 'integer|between:1,9',
    ];
}
```
(数値は半角)
![タイトル入力フォーム_requiredバリデーション_日本語化_例2_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512880/183f78ab-d946-c83f-03f0-b027d339b35a.png)
（例2）`CreateMessageRequest.php`で`boolean`を指定した場合
```diff_php:CreateMessageRequest.php
// 該当部分を抜粋
public function rules()
{
    return [
-        'message' => 'required|integer|max:10',
+        'message' => 'boolean',
    ];
}
```
(true=1、false=0)
![タイトル入力フォーム_requiredバリデーション_日本語化_例1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512880/11e1404b-5dd7-1e30-5c99-1c27bd9761e2.png)
（例3）`CreateMessageRequest.php`で`string`を指定した場合
```diff_php:CreateMessageRequest.php
// 該当部分を抜粋
public function rules()
{
    return [
-        'message' => 'required|integer|max:10',
+        'message' => 'string',
    ];
}
```
（何も入力せずに送信）
![タイトル入力フォーム_requiredバリデーション_日本語化_例2_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512880/f0430858-2931-d259-fdb7-cdf3ca07dba2.png)

##### （補足）フォームでも入力値必須の設定
これまでの手順ではPHP/Laravel側で入力値必須の設定を行いましたが、ビュー(blade)でHTMLの記述を行うことで、フォーム自体に設定することもできます。
以下の通りbladeファイルのフォーム部分に`required`属性を付与すると、未入力送信時に画像のようなエラーメッセージが表示されます。
このようにバリデーションと併せてフォーム自体の設定もしておくと、意図しないデータの入力・送信をより確実に防ぐことができます。
```diff_php:message.blade.php
<!-- 該当部分を抜粋 -->
{{ Form::open(['route' => 'postMessage']) }}

    {{ Form::label('message', 'メッセージ：') }}
-   {{ Form::text('message', null) }}
+   {{ Form::text('message', null, ['required']) }}
<!-- <input required name="message" type="text" id="message">と同様 -->

    {{ Form::submit('送信') }}
    
{{ Form::close() }}
```
![タイトル入力フォーム_requiredバリデーション_補足.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2512880/744ca47e-c295-5bcc-f97a-6d2e0eeba919.png)

# まとめ
入力フォーム作成とバリデーションの設定は、開発業務では頻出事項だと思います。
今回はデザインを考慮しませんでしたが、ユーザーの分かりやすさ・使いやすさを追求すると、さらに考えるべき点が出てくるので奥深いです。
なお、基本さえ押さえれば実装自体はそこまで難しくないので、ぜひ慣れておきましょう！

本記事が少しでも読者の皆様の参考になれば幸いです。

それでは良き開発ライフを！！

# 参考情報
Laravel公式

https://readouble.com/laravel/9.x/ja/validation.html