Not a long time ago I’ve already written about different ways of data validation in Laravel applications. Today I would like to show how you could use multiple Form requests classes when you handle more complex scenario.
Let’s assume we have website where we allow to create multiple types of user. For example we might have regular users, sellers and partners. Let’s assume some parts of registration are quite common and we want to handle registration in single controller method. Each type of user has of course different registration fields. Let’s asume that:
- regular users have only email and password fields
- sellers have company, email and password fields
- partners have partner_number and password fields
Of course in real world scenario there would be much more fields than those but that number is quite enough just to present the idea.
Single Form request class validation
Because we assumed we need to use single controller method, we should also have single Form request class to handle the validation. Let’s see how this method could look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; class RegisterRequest extends FormRequest { public function authorize() { return true; } /** * {@inheritdoc} */ public function rules() { $rules = [ 'type' => [ 'required', Rule::in('user', 'seller', 'partner'), ], ]; switch ($this->input('type')) { case 'user': $rules += [ 'email' => [ 'required', 'email', ], 'name' => [ 'required', 'string', ], 'password' => [ 'required', 'string', 'min:8', ], ]; break; case 'seller': $rules += [ 'company' => [ 'required', 'string', ], 'email' => [ 'required', 'email', ], 'password' => [ 'required', 'string', 'min:8', ], ]; break; case 'partner': $rules += [ 'partner_number' => [ 'required', 'string', ], 'password' => [ 'required', 'string', 'min:8', ], ]; break; } return $rules; } } |
First of all, notice that we assumed we have addtional type field. It’s quite reasonable to have type field sent based on chosen user type during registration.
But as you see, this class is already quite long and remember we used here very low number of fields. What if we offer more than 3 types of users? As you see it would get a bit complicated and this switch would get much more complex.
Of course, we could still improve it, extracting rules for different types of user to additional methods but it would still mean that we would have quite long Form Request class.
And how we could use this form request class in our controller? Well, we just inject this like this:
1 2 3 4 |
public function store(RegisterRequest $request) { // here we put registration logic } |
Multiple Form request validation classes
What if we could extract validation rules of each user type into separate class? This would make our classes much shorter and we would have validation for each user type in separate class. So what do we need?
First of all, we should make sure we have type field. This is special field and we are going to use custom Form request classes based on this field, so let’s start with simple Form request that will handle validation of this single field:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; class RegisterTypeRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'type' => [ 'required', Rule::in('user', 'seller', 'partner'), ], ]; } } |
As you see, it’s very simple, and we would just use this in our controller method like this:
1 2 3 4 5 6 |
public function store(RegisterTypeRequest $request) { // here we should somehow validate other parameters // here we put registration logic } |
The problem is that we cannot inject further Form request classes into our method conditionally, so we should somehow create them into controller method. The key here is to understand how Form validation request classes are handled. Each Form request class implements Illuminate\Contracts\Validation\ValidatesWhenResolved interface and it makes that whenever Form request object is created then automatically validation is made. So in fact if we would like to, we could even run validation like this:
1 2 3 4 5 6 7 8 |
public function store() { $request = app()->make(RegisterTypeRequest::class) // here we should somehow validate other parameters // here we put registration logic } |
The effect would be exactly the same. So how we could use this now? Well, we could resolve request class based on parameter type we have like this:
1 2 3 4 5 6 |
public function store(RegisterTypeRequest $request) { $userRequest = app()->make('\App\Http\Requests\'.ucfirst($request->type)); // here we put registration logic } |
Having code like this first RegisterTypeRequest class validation would be run and in case if validation passes (we have valid type field value) then validation for specific type of user would be run. The last step is creating Form requests for each user type.
For regular users validation would look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class UserRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'email' => [ 'required', 'email', ], 'name' => [ 'required', 'string', ], 'password' => [ 'required', 'string', 'min:8', ], ]; } } |
For sellers it would look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class SellerRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'company' => [ 'required', 'string', ], 'email' => [ 'required', 'email', ], 'password' => [ 'required', 'string', 'min:8', ] ]; } } |
And finally for partners it would look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class PartnerRequest extends FormRequest { public function authorize() { return true; } public function rules() { return [ 'partner_number' => [ 'required', 'string', ], 'password' => [ 'required', 'string', 'min:8', ], ]; } } |
As you see, it’s pretty clean now. For each user type we have now really clean validation class and we don’t need now any additional switches.
Summary
In this article I showed you how you can use multiple Form requests in case you want (or need) to handle multiple cases validation for single controller method. I hope you enjoyed it and now you really think using Form request is much better than using validation directly in controller
Arek
Genialne ! 😀 szukałem czegoś podobnego do obsługi różnego typu danych, która przyjmuje jedna metoda w zależności od warunku określonego przy wywołaniu. Proste, a jakie genialne. Dziękuję ! :))
Marcin Nabiałek
Dzięki, wielokrotnie korzystałem już z tego rozwiązania.