Are you looking for a slick way for validating your incoming requests on your PHP web application? Or you are already using Laravel's Request Validation, but you want to have deeper understanding on what is going on? Then you are at the right place, because I will try to break this down and explain how the Laravel's Request Validation works under the hood.
History and background
Before we get into details and concrete examples, I think it is a good idea to explain what is the Request Validation. In my opinion, the name is self-explanatory. Simply put, we have an incoming request to our application, and input we receive should match certain rules (validate it) in order to continue and process the request.
If this is a request on our login/registration page, we need to make sure that we have, for example, required values for username and password.
If this is a request on our administration dashboard, probably we will need some pagination parameters. It doesn't matter of which CRUD method type is the request, at some point we will have to do some validation on it.
Let me provide a bit history. Before the time of modern frameworks, things were not that well organized. It required a bit more lines of code to be written in order to do simple validation of the incoming data (even though today there a more files of code instead of lines doing that for you, but at least you do not have to maintain it as part of your application). I have a simple registration attempt validation from a very old application that was refactored.
Here we can see simple validation for attempt to register a user using a simple form that consists of
password and a
password_confirmation that should match the
password field. At first look this doesn't look that bad, but if we had 10 more fields, and validating for instance
password strength, length,
username rules to have only certain characters, length, to be unique, the options are limitless. You might have also seen this picture. It is valid representation, and I would highly recommend to avoid this.
If you don't understand where are the
$_SESSION variables coming from, you should probably check this out and my suggestion would be to start learning plain PHP code, and then compare how things are improved and handled in modern frameworks. In my opinion, that is the way to learn how things work and understand why things are built in the way they are. Just my two cents.
Nowadays, most of the modern frameworks will include some sort of "validation package", that will give you convenient way to validate the incoming requests. Since I have been working with Laravel for a while now, I will just show example how things improved for me when I switched over to Laravel 4.2 from validations similar to the example above. Please note that this example is for Laravel 4.2 (since 2014), and Laravel is now at version 8.x.
So framework does really help to have a good and easy validation. Everything looks nice and verbose. We are creating a
$validator instance, which will use the input from the request (basically using the data from
$_GET variables behind the scenes to take the values), and we apply some rules to check that
password are mandatory fields,
username should be only with alphabetic characters and to not exists in the
users table (based on the
unique:users rule), the
password should be minimum 8 characters and confirmed with a
password_confirmation field. You can check the validation options here. So it is really big improvement. But this doesn't seem magical at all, since it makes sense what is going on, it is verbose. But let's see the latest implementation of the request validation.
So even less lines in the controller. Now it is clear when we first see the controller, that we just work with the data (
$attributes) which has been previously validated. And we can always trust this. But let's also see what do we have in the
CreateNewUserRequest, since we don't see the previously defined rules anywhere now.
So one method
rules which should return an array of rules. We have dedicated class that has only one job, to validate the request somehow. Now the
RegisterController doesn't know nothing about how the request is validated, how the
Validator is created. The controller can just take the validated input, and probably pass it to some business logic layer to register the user.
Please note that this is not the only way to validate using the Laravel framework, nor this is not the only validation strategy. You can check the Laravel's Validation here.
But since we don't see the
rules() method being called anywhere in the example, let's dig into the Laravel's source code and figure out how and when the request is validated.
Dependency Injection via method
If you are familiar with Dependency Injection, you are probably now ahead of the curve, and you are aware that the
CreateNewUserRequest is resolved using the Laravel's Service Container. And yes, that is correct,
CreateNewUserRequest is injected in the
store method using Dependency Injection, but we are never using the
rules() method is nowhere called in our application, but if we try and hit our endpoint to register new user, it will fail due to validation error. Let's verify that.
First let's define API endpoint that is calling the controller
store method. I am using API example just because I find it easier and more understandable to explain the error and validation instead of using redirects on error, but the example applies on both.
And for the sake of simplicity, the response would be validated attributes from the request.
So, just a quick conclusion, even though we do not need to call the
rules() method from the
CreateNewUserRequest, the request is being validated. So let's check if we provide correct attributes, just a sanity check.
Please note that the
unique:usersrule will require database connection set in order to check the database for the
As seen above, everything seems to work properly. Request is being validated, and the validated attributes are returned as response. Please note that the
just_extra_field is not in the response since it has no validation defined in the
rules() method. That is just something that the
validated() method is doing for you.
Let's see now how this magical feature works behind the hood
Laravel's FormRequest class
Before we start digging into the Laravel's source code, let's just double check and see what we know from the code we wrote.
CreateNewUserRequestclass is extending the
Illuminate\Foundation\Http\FormRequest. This is what we know from Laravel's doc
- We have
rules()method that is being called somewhere when resolving the
CreateNewUserRequest. This validation is executed before the
storemethod starts with execution from the
- We have the
validated()method called inside the
Okay so, let's start by double checking the first obvious thing, the
FormRequest class from Laravel.
The first easy clue to track down and find, is the
validated() method. This method doesn't exists on our
CreateNewUserRequest, so that means it is coming from the
FormRequest. And we already know that we extend the
FormRequest with our
As it seems in the code, the validator instance is already there, and
$this->validator->validated() will only return the validated values of the rules we defined. But we are on the right path, since we found Laravel's Validator instance, so that means somewhere before this, the rules were used to create this instance. Let's check the usages of
If you scroll bit down from the
validated() method inside
FormRequest there is the
setValidator(Validator $validator) method defined.
Since this method accepts whole instance, seems like tracking this method down will show how the validator instance was made, and where the
rules() method is used in the process. After checking the usages of the
setValidator method, it will give only one place where this method is used, and that is inside the
With a quick look at the method, seems like this is the place where the validator is being instantiated, and it seems like it is not that simple, since there are some if conditions. Let's see what we have on our plate now.
- Seems like first there is a check if
$this->validatoris already instantiated. If it is, just return the instance, no need to re-create it again.
$factoryis the Laravel's Validator Factory, which know how the validator instance is being created. So this is needed to create the instance.
- If there is a method on
validator, call that method, since Laravel will expect that we are going to create the validator on our own. In the process, is passing the
- If there is no method called
validator, just use the default validator creation process from Laravel using the
- After the validator is created, check if it the method
withValidatorexists. This is useful for applying extra configuration or tweaking on the created
- Then we see the method that led us here at the first place. The
$validatorproperty is being set.
- Just return the created instance.
I know it is obvious, but I would like to state the obvious. The
$this variable is basically instance of
CreateNewUserRequest, but since we extend the
FormRequest, it inherits the capabilities.
Okay so, we know for a fact by just looking at the
CreateNewUserRequest class, that we do not have the method
validator. So we are not creating this instance on our own (even though Laravel is exposing this methods just to give more flexibility based on your needs, but we are not here for that). That means our validator instance is created using the
createDefaultValidator method. But let's do quick sanity check.
If I set a xDebug breakpoint, or just simply put a
dd('here') on line
FormRequest:84 we are going to see that it will actually trigger this logic.
There we go. Let's see the
createDefaultValidator method and finally see how the
rules method is being called.
This seems straight forward. We have the validator factory that if you check the Laravel's Validation Documentation, you will see pretty much the same thing. In order to have a validation we need:
- Data that we need to validate. That is handled using the
validationDatamethod, from which we can see it is using everything that is passed as payload or query parameter calling the
- We need set of rules. And this is the part that should be explained better.
- Error messages when validation fails. Laravel is already having default message for each validation rule that they provide. In this case it is empty, since this is meant for custom messages for this validation only. You can override this method in your request class if you need some extra flexibility. The default ones are gonna be used by default from
resources/lang/en/validation.php. You can read more here.
- Custom attributes names. If you have a attribute called
really_long_and_non_meaningful_name, you can provide a meaningful name for this attribute here, when the validation fails and maybe call it
Nice attributeinstead. You can override this method in your request class if you need some extra flexibility. This is empty since by default we do not need any custom attribute names. You can read more here.
We have all the cards on the table now, and we know what is going on here. We can focus on how the
rules method is being called right now. We can notice that the
rules method is being called using the
$this->container->call() method. That means Laravel's IoC Container (or Service Container) will decide how to make the call to this method. If you need some explanation on Dependency Injection and how the IoC Containers work behind the scenes, you can also check my other post where I go into details on this subject.
Let's do quick sanity check. Let's put
dd(this->container->call([$this, 'rules'])); on line
FormRequest:104, and check the output. According to what we saw, this should return the array of validation rules defined inside
So that is right. We see exactly what we expected. So why this is the case, why calling the method using the container, where you can simply do
$this->rules() and do the trick? It will give us the exactly same result.
Using the container vs direct call on the
If we repeat the sanity check from above, and instead of
dd($this->container->call([$this, 'rules'])); we simply put
dd($this->rules()) the outcome is going to be 100% the same. The rules are going to be returned, and nothing will change. So what is so special about the
To explain that, let's have a quick example with a small problem that should be solved. Let's say we have config file
config/settings.php. In this file we just define some rules about our application, and for example we define that the only business types values we want to store to our database are
company. We would like to validate for this values as well. But since having this in one place, it is easy, since you might hardcode this rules and not having the need of the config, but if this defined settings should be used in multiple request classes, than it is a problem, and the best way would be to use the config values as source of truth. So I am assuming as a Laravel user, first thing that will come in mind is to do the following:
And then the request
And that works just great. But there is one problem with this approach. To be honest, I really... let's say I am not a fan of the Laravel's global state. I think it is great to help beginners start building something awesome, learn, or just make fast MVPs. But I wrote previously about Dependency Injection, and now I am just using global functions. Not cool.
Let's see how can we improve this, and explain the
$this->container->call() method that is used to get the rules to validate the request.
Much better. So, using the container to call the
rules() method from our
CreateNewUserRequest instance, is allowing us to easily use Dependency Injection on the
rules() method. If for some reason, Laravel's form request was using
$this->rules() instead of
$this->container->call([$this, 'rules']);, then this wouldn't be possible. And this is giving great flexibility while validating your request. In this example I am using
Illuminate\Contracts\Config\Repository as ConfigRepository for injection, but you can find the Laravel's contracts reference here, if you are not sure which interface you should inject. Or even check the facades reference here, but keep in mind that these are the concrete classes, you should check which interface are they implementing, if any. And of course, this also applies for your own classes or interfaces, you can inject anything that is resolvable by the container.
Okay so, quick recap before we continue.
- We define the validation rules in
- We inject the
FormRequestis extending the main Laravel's request object
FormRequestholds the logic to figure it out how it will create the validator (by calling child's class
validator()method if defined, or to create the default validator)
FormRequestwhile creating the validator will use the container to call the
[email protected]or any other class that is extending the
- Since the container is responsible for calling the
rules()methid, Dependency Injection is also supported.
But let's see how the request validation is triggered without us having to do that manually. We saw that our only job is to inject the request class into the controller's method, and that's it. The request will be validated while the request class is being resolved, and the controller method will not even be triggered until the validation passes.
The magic behind the automatic request validation
In order to find out the logic responsible for request validation when the request class is being resolved, we are going to go through some of the Laravel's Service Providers, or to be more specific
Let's start with what is familiar for us, the
FormRequest class. We can see that inside the
$this->app->resolving method, it is defined that when the class name
Illuminate\Foundation\Http\FormRequest is resolving (since our request class is extending
FormRequest), the instance for this class name should be a
FormRequest class, which is created using the already existing incoming request instance (this is resolved before the
resolving method is called for
FormRequest). After the
FormRequest class is instantiated, container and redirector are passed to the instance using Dependency Injection via setter method. We already know one of the use case why the container is needed (calling the
So that seems straight forward, creating the instance, injecting some required instances, and that is our
FormRequest instance. But then, we never mentioned before the
Illuminate\Contracts\Validation\ValidatesWhenResolved interface, and we see that something is being done here with it. So let's see how this interface is actually used.
When we check the
ValidatesWhenResolved interface, there is only one method defined called
validateResolved. If we check the
FormRequest we are going to notice that it actually implements the
ValidatesWhenResolved interface, and if we try to find the
validateResolved method on the
FormRequest, we are going to find the implementation for this inside the trait
Illuminate\Validation\ValidatesWhenResolvedTrait that is also being used in the
And the trait's logic
prepareForValidationacts like a before validation hook, we can extend this method for example on
- Just like the
passesAuthorizationwill try to call
authorize()method on your request class, if this method is defined. You can check the usage here.
- If the method
authorize()exists, and return false, then the
failedAuthorizationwill throw exception which results in
403 ForbiddenHTTP response
$instanceis the validator, that we explained above how it is instantiated. So it will take all data from the request and the rules defined in our request class, and check if the validation is correct.
- If the validation fails,
failedExceptionwill throw exception that will result in
422 Unprocessable EntityHTTP response.
- Similar to
passedValidationacts like after validation hook.
Request validation Lifecycle
So just to sum it up. We define our route and the controller method that is responsible to handle the request. On our controller method, we inject the request class where we have defined our rules and authorization. We are now set to handle the request. When we have incoming request, Laravel's router will try to resolve the controller's method dependencies. One of the dependency is our request class, which when is resolved, based on the after resolving rule that is set in the
FormRequestServiceProvider for the
ValidatesWhenResolved interface, that is implemented by
FormRequest which is extended by our request class, it will call the
validateResolved method which is located in the
ValidatesWhenResolvedTrait and used in
FormRequest as well. The
validateResolved method will call the methods to try and authorize and validate the request. If any of this fails, it will results in exception. If the validation passes, then rest of the dependencies (if any) are going to be resolved from the controller's method, and the controller's method will eventually be executed. And that's basically it.
Please keep in mind that this is not the only method to validate request in Laravel. Always refer to the Laravel's documentation and use whatever suits the best in your case. I find this request validation method really useful, and in my opinion, when broken down to tiny details it seems really simple, but it gives huge benefits while building your app. And yes, there is no magic into the request validation (or any other feature in general), only a lot of hours and effort to give that feel, since is really simple and easy to use.