世界中大変なことになっていますね。私が住んでいるところも、子供たちの学校は4月いっぱい休み、レストランやバーはテイクアウトのみとなっています。それらのビジネスに携わっている人たちは本当に困っていると思います。逆に、私を含めてこのブログを読んでくれる人たちは、インターネットのおかげで忙しくなっているくらいと思います。その有難さを胸に、日々精進と行きましょう!

今回は、またもやFormRequestの話で、1つのコントローラーにおいてあるいは複数のコントローラでどうバリデーションのルールを共有の仕方を考えてみます。

ルールを共有する必要性

ルールをコントローラ間で共有する必要があるのは、重複の定義をせずに一か所で管理をして間違いを防ぎたいからです。

会員レコードを例にとると、

ユーザー画面での会員登録
ユーザー画面で名前や住所などの会員情報更新
ユーザー画面でパスワード変更
さらに、管理画面ではユーザー画面と同様に、新規、更新、パスワード更新の機能が必要となります。

つまり、少なくともユーザー画面と管理画面で2つのコントローラーが必要で、それぞれの入力画面に異なるFormRequestを作成すれば6つ必要となります。そして、それぞれにルールの定義があるわけですが、いくつかは明らかに同じものを定義することになります。将来の管理性を考えるとどうかしたいですね。

ルールの共有の仕方

ルールを共有するにはいくつかのやり方ありますが、ここでは、以前書いた以下のブログを利用してみます。

コントローラー中でのルールの共有

そこでは、1つのコントローラ内でのルールの共有していますが、今度はFormRequest内でルールを共有します。そのために、先に書いた6つのFormRequestを作成する代わりに、1つのFormRequestを作成してそれを複数のコントローラのメソッドで共有とします。

まず、以前のコードを以下に再掲載します。

namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
 
use App\User;
 
class UserController extends Controller
{
    public function rule($action, User $user = null)
   {
        // 共有するルール kana, mailcode, phone_with_dashはカスタムバリデーター
        $rules = [
            'name'        => 'required',
            'name_kana'   => 'required|kana',
            'mailcode'    => 'required|mailcode',
            'prefecture'  => 'required',
            'city'        => 'required',
            'address1'    => 'required',
            'phone'       => 'required|phone_with_dash',
        ];
 
       // 共有しないルール
       switch ($action) {
            case 'store': // レコード作成のためのルールを追加
                $rules['password']      = 'required|min:8|max:20|confirmed';
                $rules['email']         = 'required|email|unique:users';
                break;
 
            case 'update': // レコード編集のためのルールを追加
                $rules['email'] = [
                    'required',
                    'email',
                    Rule::unique('users')->ignore($user->id), //現在のレコード以外のレコードで重複がないかチェック
                ];
                break;
        }
 
        return [
            // rules
            $rules,
            // messages
            [
                'password.min' => '8から20文字長でお願いします',
                'password.max' => '8から20文字長でお願いします'
            ]
            // attributes
        ];
    }
...
    public function store(Request $request)
    {
        $request->validate(...$this->rules('store'));
        ...
    }
...
    public function update(Request $request, User $user)
    {
        $request->validate(...$this->rules('update', $user));
        ...
    }
...
}

FormRequestを使用すると、rules()の部分がUserFormRequestに移って、

...
    public function store(UserFormRequest $request)
    {
        ...
    }
...
    public function update(UserFormRequest $request, User $user)
    {
        ...
    }
...
}

となりますが、問題は、上と違って$action$userなどのパラメータを渡すことができません。どうしたらよいでしょう?

そこで登場するのが、$this->route()のメソッドです。UserFormRequestの中で、以下のようにそれらの情報が取り出せます。名前付きのルートがわかれば、それで条件分岐できます。

        $route= $this->route()->getName(); // 現在のrouteの名前を取得
        $user = $this->route('user'); // update(UserFormRequest $request, User $user)の$userを取得

以下が、UserFormRequestのコードです。

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UserFormRequest 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 rule()
    {
        $route= $this->route()->getName(); // 現在のrouteの名前を取得
        $user = $this->route('user'); // update(UserFormRequest $request, User $user)の$userを取得

        // 共有するルール kana, mailcode, phone_with_dashはカスタムバリデーター
        $rules = [
            'name'        => 'required',
            'name_kana'   => 'required|kana',
            'mailcode'    => 'required|mailcode',
            'prefecture'  => 'required',
            'city'        => 'required',
            'address1'    => 'required',
            'phone'       => 'required|phone_with_dash',
        ];
 
       // 共有しないルール
       switch ($route) {
            case 'user.store': // レコード作成のためのルールを追加
                $rules['password']      = 'required|min:8|max:20|confirmed';
                $rules['email']         = 'required|email|unique:users';
                break;
 
            case 'user.update': // レコード編集のためのルールを追加
                $rules['email'] = [
                    'required',
                    'email',
                    Rule::unique('users')->ignore($user->id), //現在のレコード以外のレコードで重複がないかチェック
                ];
                break;
        }
 
        return $rules;
    }

    public function messages()
    {
        return [
                'password.min' => '8から20文字長でお願いします',
                'password.max' => '8から20文字長でお願いします'
        ];
    }
}

By khino