【Laravel】コントローラの一つのメソッド内で使うフォームリクエストを切り替える

  • 2021年2月17日
  • 2021年2月17日
  • Laravel

 Laravel は PHP フレームワークであり多くの場合、 web サイトの作成で使われます。Laravel にはクライアントからのリクエストを制御する機能が多く備わっておりフォームリクエストはその一つです。
バリデーション 6.x Laravel#フォームリクエストバリデーション
 フォームリクエストは次の様に使うとリクエストの内容を自動でバリデーションしてくれます。

class PostStoreRequest extends FormRequest
{
	public function rules()
	{
	    return [
	        'title' => 'required|unique:posts|max:255',
	        'body' => 'required',
	    ];
	}
}

class PostController extends Controller
{

    public store(PostStoreRequest $request)
    {
        // 引数の型を元にリクエストがPostStoreRequestインスタンスになり、バリデーションが自動で走ります。
        // バリデーションエラーがない場合のみ本処理に入ります。
    }
}

 時としてログインしているユーザやリクエストの一部、データベース等の HTTP 外のリソースによって適用したいバリデーションが大きく変わる時があります。小さく変わる時は次の様にフォームリクエスト内に制御を書くので十分ですが、大きくなるとクラス自体を変えたくなります。

class PostStoreRequest extends FormRequest
{
	public function rules()
	{
	    $baseRule = [
	        'title' => 'required|unique:posts|max:255',
	        'body'  => 'required',
	    ];
	    $userRule = array_merge($baseRule,[
	        'author' => 'nullable|string|max:255',
	    ]);
	    $adminRule = array_merge($baseRule,[
	        'author' => 'nullable|string|max:255',
	        'display' => 'nullable|boolean',
	    ]);
	    $user = $this-user('web');// 認証状態のユーザインスタンスをこのメソッドで呼べます。Laravel組み込みメソッドです。
	    if($user->type === User::TYPE_GENERAL_USER){
	       return $userRule;
	    }
	    if($user->type === User::TYPE_ADMIN){
	       return $adminRule;
	    }
	    
	    return $baseRule;
	}
}

 そういった時は次の様にすると便利です。

use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;

/** 他フォームリクエストの親クラス */
abstract class BaseFormRequest
{
    /**
     * Illuminate\Http\Request インスタンスを元にフォームリクエストを生成するメソッド
     * @param  Request                    $from
     * @param  null                       $to
     * @throws BindingResolutionException
     * @return BaseFormRequest|Request
     */
    public static function createFrom(Request $from, $to = null)
    {
        // 親の createForm を呼びます
        $request = parent::createFrom($from, $to);
        // 残念なことに Laravel 的に生焼けオブジェクトなので 
        // Laravel の DI(dependency injection)コードを参考に必要なパラメータをセットします。
        // 該当コードは Laravel 6.20.16 では次の場所にあります。
        // @see vendor/laravel/framework/src/Illuminate/Foundation/Providers/FormRequestServiceProvider.php:33
        $app     = app();
        $request->setContainer($app)->setRedirector($app->make(Redirector::class));

        return $request;// 完成したインスタンスが返ります。
    }
    
    // rule メソッドを必須化しておくと IDE でスカフォールドができたり、スペルミスがなくなり便利です。
    abstract public function rules(): array;
}
// 大体 PostStoreRequest として共通なので更に中小クラスを挟みます。attributes メソッドなんかは完全に同じになりやすいですabstract class PostStoreRequest extends BaseFormRequest{/* 投稿リクエストであれば必ずある処理や定義を記述 */}
// ↑の親クラスを継承した各フォームリクエストクラスを作ります
class PostStoreRequestAsGuest extends PostStoreRequest{/* 省略 */}
class PostStoreRequestAsUser extends PostStoreRequest{/* 省略 */}
class PostStoreRequestAsAdmin extends PostStoreRequest{/* 省略 */}

 ↑で定義したフォームリクエストクラスを次の様にコントローラで呼び出します。

use Illuminate\Http\Request;

class PostController extends Controller
{

    public store(Request $request)
    {
        $user = auth('web')->user();
        // 条件分岐でベースとなる Illuminate\Http\Request から各フォームリクエストクラスインスタンスを生成
        if($user->type === User::TYPE_GENERAL_USER){
	       $request = PostStoreRequestAsUser::fromCreate($request);
	    }else if($user->type === User::TYPE_ADMIN){
	       $request = PostStoreRequestAsAdmin::fromCreate($request);
	    }else{
	       $request = PostStoreRequestAsGuest::fromCreate($request);
	    }
	    // フォームリクエストのバリデーション機能を起動
	    // バリエーションエラーが発生した場合、バリエーションエラー例外が投げられます。
	    $request->validateResolved();
	    
	    // ここから↓にフォームリクエストが引数になった場合同様のいつも通りの処理
    }
}

 こうすると条件分岐で膨れ上がったフォームリクエストクラスを避けることができ、かつリクエストのバリデーションとその後の処理にフォームリクエストクラスの機能を使えます。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG