【Laravel】データの登録時と編集時でバリデーションルールを変えたいときの実装方法

# はじめに
この記事は[Laravel #2 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/laravel-2)の18日目の記事です
ちなみに初アドベントカレンダーです。お手柔らかに:innocent:

# TL;DR

Laravelでデータの登録時と編集時でバリデーションルールを変えたいときの実装方法で
こんな感じで実装してみましたという
記事です。(三行)


# Requestクラスにバリデーションルールを定義してしまうと色々つらい

**Request**クラスにバリデーションルールを定義するのが一般的なやり方（？）だと思うのですが、
もし、**データ編集時はこのバリデーションルールを適用してくない！**、だとか、**海外ユーザーの場合は全く異なるバリデーションルールを適用したい！**、という場合にデータ登録時同一のバリデーションルールが適用されるとなるとつらいものがあります。
なので、いっそのことバリデーションルールをモデルの方へ定義することにしました。

# 実装例のケース

例としてここでは**管理者(Admin)**が**ユーザー(User)**を**登録・編集**する際のバリデーションルールを例に書いていきます。
ちなみにリクエストパラメータは以下の感じを想定しています↓

```
array (size=2)
  '_token' => string '****************************************' (length=40)
  'user' => 
    array (size=3)
      'name' => string '田中' (length=6)
      'username' => string 'tanaka' (length=6)
      'password' => string 'password1234' (length=12)
```

こんなパスワードは例だけにしておいてください:innocent:

## **Request**クラスの実装例

**rulesメソッド**でリクエストメソッドから登録時か編集時かの切り分けを行っています。
Userを登録する際のバリデーションルールはRequestクラスに定義せず、モデルに定義するようにします。

```php:UserRequest.php
<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use App\User;

class UserRequest 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
     */
    public function rules(Request $request)
    {
        // 編集時は異なるバリデーションルールを適用する
        if ($request->method() === 'PUT') {
            return User::validationRulesForEdit($request->all());
        }

        return User::validationRules();
    }

    public function messages()
    {
        return [
            'user.name.required'     => '名前を入力してください',
            'user.loginid.required'  => 'IDを入力してください',
            'user.password.required' => 'パスワードを入力してください',
            'user.password.min'      => 'パスワードは8文字以上必要です',
        ];
    }
}
```

## Modelの実装例

Modelに登録時のバリデーションルールと編集時のバリデーションルールを定義するよにしています。

```php:User.php
<?php
namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'loginid',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * バリデーションルール
     *
     * @return array
     */
    public static function validationRules()
    {
        return [
            'user.name'     => ['required'],
            'user.loginid'  => ['required'],
            'user.password' => ['required', 'min:8'],
        ];
    }

    /**
     * 編集時のバリデーションルール
     *
     * @param array $requestAll
     * @return array
     */
    public static function validationRulesForEdit(array $requestAll)
    {
        $editRules = [];

        // パスワード（編集時は空の場合は許可するルール）
        if (array_key_exists('password', $requestAll['user']) === false || strlen($requestAll['user']['password']) === 0) {
            $editRules['user.password'] = ['sometimes', 'present'];
        }

        return array_replace(self::validationRules(), $editRules);
    }
}
```

これで、登録時と編集時で好きなだけバリデーションルールを変えることができます。

# パラメータによってバリデーションルールを変える場合

もしパラメータによってバリデーションルールを変える場合（country値がjapan以外とか）の場合は
**Request**クラスの**rulesメソッド**でリクエストパラメータを参照して適用するバリデーションルールを変えてあげればいいのかなと。

```php:UserRequest.php
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules(Request $request)
    {
        // 海外ユーザーの場合
        if ($request->all()['country'] !== 'japan') {
            return User::validationRulesForOverseas($request->all());
        }

        return User::validationRules();
    }
```

↑あんまりきれいとは言えないけどこんな感じですかね・・・


# おわり
- この方法が最善かはわかりません:wink:
- Laravelつよつよエンジニアの方もっといい方法があれば教えてください:bow: