Taking control of Laravel FormRequest Response
Laravel FormRequest provides a way to handle form validation in a separate class file without cluttering your controller. Keeping your controller slim has many pros which include testable and maintainable code.
Most Artisans still prefer to place validation logic inside a controller method because they wish to have total control over validation failure response and redirection. In this post, I will show you how to use FormRequest and still have full control over responses.
Although Laravel FormRequest handles validation and automatically return appropriate response such JSON or redirect to a form or another page with error messages, you might have a specific situation where you wish to have full control over what is being returned.
Well, you don’t need to be bitten by a spider before you gain superpowers, One good thing about Laravel is you can easily customize or extend various functionalities.
Let’s dive into examples.
In this example, I will show you how to use FormRequest in the LoginController login method. The purpose of this validation is to ensure the user provides an email and password, it does not check if the credential is valid.
Let us get started by creating a form request. You can easily create a form request with make:request
Artisan command
php artisan make:request LoginRequest
The command above generates and place loginRequest.php
in app\Http\Request
directory
Let’s customize our form request by adding validation rules and modifying validation failure response.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required|string',
];
}
/**
* Handle a failed validation attempt.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
protected function failedValidation(Validator $validator)
{
if($this->wantsJson())
{
$response = response()->json([
'success' => false,
'message' => 'Ops! Some errors occurred',
'errors' => $validator->errors()
]);
}else{
$response = redirect()
->route('guest.login')
->with('message', 'Ops! Some errors occurred')
->withErrors($validator);
}
throw (new ValidationException($validator, $response))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
}
As you can see above, we imported two classes
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Validation\ValidationException;
Validator class is used for request validation, you could use the helper function validator()
for convenience. ValidationException is required as it throws an important exception
We set authorization to true by returning true from authorize
the method this allows any incoming form request to be validated.
public function authorize()
{
return true;
}
Please note that authorize
the method is called first and returning false throws AuthorizationException
and validation will not be executed.
Next, we overwrite failedValidation
method to enable us take control of what is returned on validation failure.
protected function failedValidation(Validator $validator)
{
if($this->wantsJson())
{
$response = response()->json([
'success' => false,
'message' => 'Ops! Some errors occurred',
'errors' => $validator->errors()
]);
}else{
$response = redirect()
->route('guest.login')
->with('message', 'Ops! Some errors occurred')
->withErrors($validator);
}
throw (new ValidationException($validator, $response))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}
You could go wild with many other responses, in this example, we simply included few custom messages in the response.
Lastly, let's attach our LoginRequest
to LoginController’s login
method
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use App\Http\Requests\LoginRequest;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
/**
* Validate the user login request.
*
* @param \App\Http\Requests\LoginRequest $request
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
protected function validateLogin(LoginRequest $request)
{
}
}
We overwrite validateLogin
method and simply type hint LoginRequest
which automatically triggers form request validation. We do not have any code in validateLogin
method since its called from login
method.
Conclusion
In this tutorial, we are able to move validation logic to the form request class and still have control over responses. We are able to include more messages in the validation failure response. We can easily maintain and test our code and reuse our validation logic. Laravel FormRequest is the way to go especially if you are building a big application.