Laravel Form Validation 101: Custom Request Validation and Rules
Request validation is very essential step when you work with the forms frequently. Users can give strange types of inputs which can cause severe error if you directly try to push the data into logics or database. So its always should be the first step to validate those form data to ensure those are coming in the expected format or type. Laravel as a php framework provides a convenient way to validate request data very easily. It has plenty of built in rules which will help you to get rid of writing any custom logic to check the forms. Besides, you can use your own custom classes and rules to go beyond that when necessary. I will be your guide today to discuss various aspects of request validation in Laravel.
Streamline Your Laravel Development with Form Validation
Lets start with installing a fresh new application in Laravel :
laravel new validation-example
cd validation-example
Now we will integrate the routes. We need 2 routes at least, one is with GET request, in which we will show our form and another will be responsible to handle POST request after submitting form. Lets modify web.php like this:
use App\Http\Controllers\FormController;
Route::get('/form', [FormController::class, 'showForm']);
Route::post('/submit', [FormController::class, 'submitForm']);
Now, lets create the controller:
php artisan make:controller FormController
Now in the FormController we created, lets add the two function pointing the corresponding route:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class FormController extends Controller
{
public function showForm()
{
return view('form');
}
public function submitForm(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'email' => 'required|email',
]);
if ($validator->fails()) {
return redirect('/form')
->withErrors($validator)
->withInput();
}
// If validation passes, perform further actions here
return "Validation Passed!";
}
}
Assume, we have 2 fields in our form - name and email.
Lets have some understanding here, firstly the showForm method will just show our form. Then, the submitForm method will handle the incoming request submitted from the form. Laravel has facade named Validator which is used here to perform the validation in the upcoming data, in it’s make method, we are passing an array with the input fields we want to validate.
For example, for name field we are assigning ‘required|string|max:255’, which means, name field should be a required field, and a string and it should have max character length of 255.
For email, with 'required|email', we are saying, the email is also a required field and data should be a type of email format.
Now, if the validator fails, means if the user gives wrong input, we are returning the user back to form view. Also we have passed the errors to show in frontend.
Alternatively, we can also do the same validation on the request like this:
public function submitForm(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email',
]);
// If validation passes, perform further actions here
return "Validation Passed!";
}
You can use any way you like.
Now, In the resources/views directory, lets add a form with with the input fields:
<!-- resources/views/form.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Validation Example</title>
</head>
<body>
<form action="/submit" method="POST">
@csrf
<input type="text" name="name" placeholder="Enter your name" value="{{ old('name') }}"><br>
@error('name')
<div>{{ $message }}</div>
@enderror
<input type="email" name="email" placeholder="Enter your email" value="{{ old('email') }}"><br>
@error('email')
<div>{{ $message }}</div>
@enderror
<button type="submit">Submit</button>
</form>
</body>
</html>
Now in the form, under each input field, we have added a blade directive @error, which is checking with the corresponding field name if there is any error passed in session. If it is, it shows in the message box by $message.
Now you can run run the application and test the validation with different data:
Php artisan serve
So its very convenient as you can see, we just put a word as validation rule without needing any extra coding or custom logics to validate all those fields. Laravel has already written it for us and we just have to know and use it.
Mastering Laravel Forms: Using Request Classes for Validation
We can perform validations in controller like above, or we can use a smart way by creating new class for the request. This is an alternative approach but very effective.
We have already installed a laravel application, and we have a form where name and email fields should be validated before being inserted to database.
Now lets create a new Request with this command :
php artisan make:request MyValidationRequest
This will create a new request class file MyValidationRequest.php under app/Http/Requests. Now please modify the codes as following:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class MyValidationRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
// Usually, you would put your authorization logic here.
// For simplicity, let's return true to authorize all requests.
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
// Define your validation rules here...
];
}
}
Here, you can see we have provided validation rules under rules() method.
First, name is required, should be string and have max character length of 255.
And email is also required, should be an email type and unique to the email column to the users table.
Then, we just have to bind this new request class to our controller’s request data. Lets update our controller method where we want to perform validation on the request:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Requests\MyValidationRequest;
class MyController extends Controller
{
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\MyValidationRequest $request
* @return \Illuminate\Http\Response
*/
public function store(MyValidationRequest $request)
{
// The request is validated before reaching this point.
// Now, you can proceed with your business logic...
}
}
As you can see, we are not using Laravel's Request facade here as usual. instead, we are directly using our own request class MyValidationRequest. This will validate the data before getting into the controller codes. And if the validations fail, it will redirect back with the error messages.
- What if I want to show custom messages?
When the validations fail, laravel actually redirects user back to the previous place with a default error message. But for your information, you can also customize this messages as your convenience like this:
// app/Http/Requests/CustomValidationRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CustomValidationRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
// Define more validation rules as needed
];
}
public function messages()
{
return [
'name.required' => 'The name field is required.',
'name.string' => 'The name must be a string.',
'name.max' => 'The name may not be greater than 255 characters.',
'email.required' => 'The email field is required.',
'email.email' => 'Please provide a valid email address.',
'email.unique' => 'This email has already been taken.',
// Add more custom messages for other rules if needed
];
}
}
Note: Please remember that, you can define messages for every single validation rule and it’s failure. For example, name field had 3 rules, required, string and max.. We have defined 3 custom messages separately for 3 different rules. Same for the email field.
We have seen 2 different way for validating forms in Laravel. Between them, I think this way is smarter to implement. Also it keeps our controller slim and beautiful, avoiding any redundant codes there. Which one do you prefer let me know in the comments please.
Laravel Custom Validation with Closures: When Built-in Rules Aren't Enough
We have explored how to work with request validation in different ways like validator facade or custom class etc, and we have also seen that laravel provides specific logic already written for as logics, so we dont have to write any custom logic everytime. Rather, we can just use single word as the name of the rule, for example, required, image, number, string etc, which is enough for validating the field. This just not only makes the code beautiful, but also helps on faster development flow. Nevertheless, we sometimes need to add custom logic which is not available in laravel’s rules list. Now we will see how we can make or use custom validation logic with closure.
- Custom validation in request
We can directly add custom validation logic to request object.
For example, you have a name field in form, and if someone types in the form ‘forbidden’ as value, you will return with error saying field invalid.
Lets see how to implement this logic:
use Illuminate\Http\Request;
public function store(Request $request)
{
$request->validate([
'name' => ['required', function ($attribute, $value, $fail) {
if ($value === 'forbidden') {
$fail($attribute . ' is invalid.');
}
}],
]);
// Continue with storing the data
}
You can see we are passing a closure as rule instead of any predefined rule name from Laravel. This closure or function is providing as parameters like $attribute, $value, $fail. Here, $attribute is the field name, $value is it’s value and $fail is the function what we can call whenever we want or a condition fails.
So as discussed, when the field has a invalid value, we are calling the $fail function and we are providing the message it will have to return with as error.
- Using Validator facade
The procedure is same if we use the validator facade too. Here is the example:
use Illuminate\Support\Facades\Validator;
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => ['required', function ($attribute, $value, $fail) {
if ($value === 'forbidden') {
$fail($attribute . ' is invalid.');
}
}],
]);
if ($validator->fails()) {
return redirect()->back()->withErrors($validator)->withInput();
}
// Continue with storing the data
}
$validator->fails() will be true if $fail method is triggered.
- Custom request class
We know how to make a custom request class right?
Lets have a glimpse. First quickly create a new request class:
php artisan make:request StoreRequest
Then again, in the rules method, we can use the same way as above to validate name field:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => ['required', function ($attribute, $value, $fail) {
if ($value === 'forbidden') {
$fail($attribute . ' is invalid.');
}
}],
];
}
}
Now bind the class with request parameter instead of default Laravel’s Request class:
use App\Http\Requests\StoreRequest;
public function store(StoreRequest $request)
{
// Validation has already been performed by the StoreRequest class
// Continue with storing the data
}
So you can understand, how smart this framework is, providing us many handy ways to achieve the functionalities we want for our app. There are plenty rules already written underneath the hood, which is mostly enough for our day to day works, but also when you build more complex application, you often need to write custom logics for validation. The closure as validation rule will help in such cases.
Laravel Custom Validation: Extending Custom Rules
For form validation, Laravel provides amazing ways to validate data with built in or custom rules. For custom rules also, Laravel has terrific support to feel flexible as a developer to choose the way you like or the way that helps your code architecture. When making custom validations, we can make our own custom rule, name it as we want, and then can use it like a built in rule in Laravel. Now, we will discuss and see example of this kind of functionality and how to use it.
- Use custom rule in Facade
We have seen in laravel different kind of validation rule for different fields right?
Here is an example:
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
We can see these words named required, string, max min, unique etc. all are rules provided default by Laravel. What if you want a custom rule name for yourself. You can also achieve that.
We will use custom rule in Validator facade.
This facade has a function called extend by which you can register a new rule as like the built ins.
Lets add a custom rule name as we want, for example ‘foo’:
use Illuminate\Support\Facades\Validator;
Validator::extend('foo', function ($attribute, $value, $parameters, $validator) {
return $value === 'foo';
});
Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) {
return str_replace(':attribute', $attribute, ':attribute must be foo');
});
// Usage
$validator = Validator::make($request->all(), [
'username' => 'required|foo',
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
// Handle valid input...
You can see, the extend method is registering the foo as rule, and in the callback function, we can access data like $attribute, $value, $parameters, $validator. We have discussed above about these parameters.
Also in the function we are passing the condition as flag, if it is true the validator passes, otherwise not.
In the replacer method, we are placing the message for the failure of the rule. Here we are giving a custom message to show as error.
- Custom validation in request class
Lets create a request class first (skip if you have already created):
php artisan make:request StoreUserRequest
For class, the process is bit different than facade way, but idea is same.
We will add a custom rule in rule method, and register the rule and it’s message in withValidation function:
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'username' => 'required|custom_rule',
'email' => 'required|email',
];
}
public function withValidator($validator)
{
$validator->addExtension('custom_rule', function ($attribute, $value, $parameters, $validator) {
return $value !== 'admin';
});
$validator->addReplacer('custom_rule', function ($message, $attribute, $rule, $parameters) {
return str_replace(':attribute', $attribute, ':attribute is invalid.');
});
}
}
From withValidator method, a custom rule will be added named custom_rule in rules.
The addExtension and addReplacer is doing the same thing as facade’s extend and replacer method respectively.
Now use this Request class as before in the controller.
- Summary
Laravel is a powerful framework with many features and options available for a specific functionality so devs can feel more flexible with his/her workflow. Request validation is also such an example. You know, in form handling, we have to validate for data and requests very efficiently, and laravel provides many ways to achieve that also. We have seen basic form validation where we can directly validate the requests in the controller, also with validator facade. Also we can now make custom request class which is more organised approach. For rules we know that laravel has already a lot of built in rules that will suffice for our regular activities. But often we may need to opt for our custom rules, which we can directly make in validator via function, or with organised approach like creating custom Rule class or extending the validator.
Thank you for your time in this post. Let me know your thoughts in the comments.
I wish you a happy day.
Comments