Are you the type of developer who likes to keep your controllers clean? Writing minimal logic without losing the functionality you strive for? Injecting Form Requests into your controllers to tuck away your validation logic is definitely for you.
This guide is going to refactor the controller based validation that we see in my guide for from a few days ago; Laravel 10: Sending Emails with Envelopes
What are Form Requests?
These are validation files that can be generated with an artisan command. This command is php artisan make:request NewMessageReceivedEmailRequest
where NewMessageReceivedEmailRequest
is the name of the class. These will check if you are authorized to even be using the validation class allowing you to handle all of the validation rules and customizable messages that are sent back to the front end in the event of a validation failure, therefore, you can imagine, placing all of this logic, in your controller would make controllers exponetially longer and more complex than needed.
So, let’s take the concept of Form Requests, and learn to understand and implement them. For this tutorial we will use above request we made with the artisan
command and validate a contact form to send an email.
Default Form Request Stub
Out of the box, when making a new Form Request, Laravel provides the basic stub you need to get you started. It looks something like the below code.
<?php
namespace App\Http\Requests\Emails;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class NewMessageReceivedEmailRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
//
];
}
}
For the most part, these two methods are all we are going to need to perform validation, especially if you plan on allowing Laravel to handle the validation messages for you. Lets take a look at each method on there own and explore what they are doing.
authorize()
method
Here we have to pass a condition that returns a boolean. It doesn’t matter how you get to that boolean, but thats whats required. If you are building a website that is completely public facing, the default return true
is likely all you need. However, if you are building an application where people are logged in, you may want to check if the user is logged in, whether they have a specific role to even be able to make this request and so on. There are various ways we can achieve these and I will explore these below.
Checking a basic user is authenticated to make request
For most basic logged in applications, the following code snippet if probably sufficient to check if a user is authenticated. If a user is not logged in, and for some reason tries to access the controller implementing this Form Request, the Request will return a false on the authorize method and the user will receive a HTTP 401 Unauthorized response.
public function authorize(): bool
{
return auth()->check();
}
Checking whether a user has a specific role or permission
In more advanced applications your users may have roles and permissions assigned to them. In this case, you are going to want to leveage the can()
method on the authenticated user. Laravel provides a nice way to do this on the auth() user.
public function authorize(): bool
{
return auth()->user()->can('send-emails');
}
Whether you are leveraging policies to handle permissions, or something like spatie’s Laravel Permissions package both of these are compatible with the can method.
rules()
method
This is a pretty self explanitory function name. This is where the rules for your form fields will need to be placed. Please note; the validation fields are taken from the “name” attribute on a html input, select textare field etc… This method expects a set of rules, returned as an array to validate the fields against. As an example see below;
public function rules(): array
{
return [
'email' => 'required|email',
'name' => 'required',
'message' => 'required'
];
}
Here we can see quite simple rules, however, Laravel has over 100+ built in validation rules that can be used out of the box, or even extended for custom functionality. This set of rules are only leveraging 2 of the 100+ laravel has, and that is making sure the fields are marked as required
and that the email field has an email address that is formatted correctly.
NOTE: Formatted correctly does not necessarily mean is a real email address. Email checks for the existance of the @ sign, a domain and TLD
Now we have our Form Request completed, Lets put it all together and import it into our controller
Inject Form Request into Controller
Now, instead of using Request $request
s the first argument inside our controller we can now use the Class of the Form Request that was just created. Remember to add it to the use...
block at the top of your controller too. Injecting the form controller this way will automagically run all data sent to the controller through your validation before any of the controller code is executed.
public function send( NewMessageReceivedEmailRequest $request )
{
try {
$validated = $request->safe();
Mail::to(config('mail.from.address'))->send(new NewMessageReceivedEmail([
'name' => $validated['name'],
'email' => $validated['email'],
'message' => $validated['message']
]));
} catch (Exception $e) {
return redirect()->back()->with([
'success' => false,
'message' => 'Email not sent!. Reason: ' . $e->getMessage()
]);
}
return redirect()->back()->with([
'success' => true,
'message' => 'Email sent successfully!'
]);
}
In addition to the data being validated before the controller is reached, to be super sure your only sending validated data to other files, you can use $validated = $request->safe()
. This will create an array of safe, validated values available for you to use and pass to other classes or functions.
Closing Words
Its great practice to get into the habit of using for requests. They can remove a lot of unnecessary logic from a controller and into the Form Request. It simplifies not only your controller, but simplifies your validation as it’s housed in a single file. If you have functions that share the same fields and validation logic, they can even be shared between functions, allowing you to develop a more DRY
environment.
Ultimately, I hope this guide helped you understand for requests.