Validation
Introduction
Laravel provides several different approaches to validate your application's incoming data. It is most common to use the validate
method available on all incoming HTTP requests. However, we will discuss other approaches to validation as well.
Laravel includes a wide variety of convenient validation rules that you may apply to data, even providing the ability to validate if values are unique in a given database table. We'll cover each of these validation rules in detail so that you are familiar with all of Laravel's validation features.
Validation Quickstart
To learn about Laravel's powerful validation features, let's look at a complete example of validating a form and displaying the error messages back to the user. By reading this high-level overview, you'll be able to gain a good general understanding of how to validate incoming request data using Laravel:
Defining The Routes
First, let's assume we have the following routes defined in our routes/web.php
file:
use App\Http\Controllers\PostController;
Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);
The GET
route will display a form for the user to create a new blog post, while the POST
route will store the new blog post in the database.
Creating The Controller
Next, let's take a look at a simple controller that handles incoming requests to these routes. We'll leave the store
method empty for now:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class PostController extends Controller
{
/**
* Show the form to create a new blog post.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('post.create');
}
/**
* Store a new blog post.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
// Validate and store the blog post...
}
}
Writing The Validation Logic
Now we are ready to fill in our store
method with the logic to validate the new blog post. To do this, we will use the validate
method provided by the Illuminate\Http\Request
object. If the validation rules pass, your code will keep executing normally; however, if validation fails, an Illuminate\Validation\ValidationException
exception will be thrown and the proper error response will automatically be sent back to the user.
If validation fails during a traditional HTTP request, a redirect response to the previous URL will be generated. If the incoming request is an XHR request, a JSON response containing the validation error messages will be returned.
To get a better understanding of the validate
method, let's jump back into the store
method:
/**
* Store a new blog post.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// The blog post is valid...
}
As you can see, the validation rules are passed into the validate
method. Don't worry - all available validation rules are documented. Again, if the validation fails, the proper response will automatically be generated. If the validation passes, our controller will continue executing normally.
Alternatively, validation rules may be specified as arrays of rules instead of a single |
delimited string:
$validatedData = $request->validate([
'title' => ['required', 'unique:posts', 'max:255'],
'body' => ['required'],
]);
In addition, you may use the validateWithBag
method to validate a request and store any error messages within a named error bag:
$validatedData = $request->validateWithBag('post', [
'title' => ['required', 'unique:posts', 'max:255'],
'body' => ['required'],
]);
Stopping On First Validation Failure
Sometimes you may wish to stop running validation rules on an attribute after the first validation failure. To do so, assign the bail
rule to the attribute:
$request->validate([
'title' => 'bail|required|unique:posts|max:255',
'body' => 'required',
]);
In this example, if the unique
rule on the title
attribute fails, the max
rule will not be checked. Rules will be validated in the order they are assigned.
A Note On Nested Attributes
If the incoming HTTP request contains "nested" field data, you may specify these fields in your validation rules using "dot" syntax:
$request->validate([
'title' => 'required|unique:posts|max:255',
'author.name' => 'required',
'author.description' => 'required',
]);
On the other hand, if your field name contains a literal period, you can explicitly prevent this from being interpreted as "dot" syntax by escaping the period with a backslash:
$request->validate([
'title' => 'required|unique:posts|max:255',
'v1\.0' => 'required',
]);
Displaying The Validation Errors
So, what if the incoming request fields do not pass the given validation rules? As mentioned previously, Laravel will automatically redirect the user back to their previous location. In addition, all of the validation errors and request input will automatically be flashed to the session.
An $errors
variable is shared with all of your application's views by the Illuminate\View\Middleware\ShareErrorsFromSession
middleware, which is provided by the web
middleware group. When this middleware is applied an $errors
variable will always be available in your views, allowing you to conveniently assume the $errors
variable is always defined and can be safely used. The $errors
variable will be an instance of Illuminate\Support\MessageBag
. For more information on working with this object, check out its documentation.
So, in our example, the user will be redirected to our controller's create
method when validation fails, allowing us to display the error messages in the view:
<!-- /resources/views/post/create.blade.php -->
<h1>Create Post</h1>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- Create Post Form -->
Customizing The Error Messages
Laravel's built-in validation rules each has an error message that is located in your application's resources/lang/en/validation.php
file. Within this file, you will find a translation entry for each validation rule. You are free to change or modify these messages based on the needs of your application.
In addition, you may copy this file to another translation language directory to translate the messages for your application's language. To learn more about Laravel localization, check out the complete localization documentation.
XHR Requests & Validation
In this example, we used a traditional form to send data to the application. However, many applications receive XHR requests from a JavaScript powered frontend. When using the validate
method during an XHR request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code.
The @error
Directive
You may use the @error
Blade directive to quickly determine if validation error messages exist for a given attribute. Within an @error
directive, you may echo the $message
variable to display the error message:
<!-- /resources/views/post/create.blade.php -->
<label for="title">Post Title</label>
<input id="title" type="text" name="title" class="@error('title') is-invalid @enderror">
@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
If you are using named error bags, you may pass the name of the error bag as the second argument to the @error
directive:
<input ... class="@error('title', 'post') is-invalid @enderror">
Repopulating Forms
When Laravel generates a redirect response due to a validation error, the framework will automatically flash all of the request's input to the session. This is done so that you may conveniently access the input during the next request and repopulate the form that the user attempted to submit.
To retrieve flashed input from the previous request, invoke the old
method on an instance of Illuminate\Http\Request
. The old
method will pull the previously flashed input data from the session:
$title = $request->old('title');
Laravel also provides a global old
helper. If you are displaying old input within a Blade template, it is more convenient to use the old
helper to repopulate the form. If no old input exists for the given field, null
will be returned:
<input type="text" name="title" value="{{ old('title') }}">
A Note On Optional Fields
By default, Laravel includes the TrimStrings
and ConvertEmptyStringsToNull
middleware in your application's global middleware stack. These middleware are listed in the stack by the App\Http\Kernel
class. Because of this, you will often need to mark your "optional" request fields as nullable
if you do not want the validator to consider null
values as invalid. For example:
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);
In this example, we are specifying that the publish_at
field may be either null
or a valid date representation. If the nullable
modifier is not added to the rule definition, the validator would consider null
an invalid date.
Form Request Validation
Creating Form Requests
For more complex validation scenarios, you may wish to create a "form request". Form requests are custom request classes that encapsulate their own validation and authorization logic. To create a form request class, you may use the make:request
Artisan CLI command:
php artisan make:request StorePostRequest
The generated form request class will be placed in the app/Http/Requests
directory. If this directory does not exist, it will be created when you run the make:request
command. Each form request generated by Laravel has two methods: authorize
and rules
.
As you might have guessed, the authorize
method is responsible for determining if the currently authenticated user can perform the action represented by the request, while the rules
method returns the validation rules that should apply to the request's data:
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
You may type-hint any dependencies you require within the rules
method's signature. They will automatically be resolved via the Laravel service container.
So, how are the validation rules evaluated? All you need to do is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
/**
* Store a new blog post.
*
* @param \App\Http\Requests\StorePostRequest $request
* @return Illuminate\Http\Response
*/
public function store(StorePostRequest $request)
{
// The incoming request is valid...
// Retrieve the validated input data...
$validated = $request->validated();
// Retrieve a portion of the validated input data...
$validated = $request->safe()->only(['name', 'email']);
$validated = $request->safe()->except(['name', 'email']);
}
If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an XHR request, an HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.
Adding After Hooks To Form Requests
If you would like to add an "after" validation hook to a form request, you may use the withValidator
method. This method receives the fully constructed validator, allowing you to call any of its methods before the validation rules are actually evaluated:
/**
* Configure the validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});
}
Stopping On First Validation Failure Attribute
By adding a stopOnFirstFailure
property to your request class, you may inform the validator that it should stop validating all attributes once a single validation failure has occurred:
/**
* Indicates if the validator should stop on the first rule failure.
*
* @var bool
*/
protected $stopOnFirstFailure = true;
Customizing The Redirect Location
As previously discussed, a redirect response will be generated to send the user back to their previous location when form request validation fails. However, you are free to customize this behavior. To do so, define a $redirect
property on your form request:
/**
* The URI that users should be redirected to if validation fails.
*
* @var string
*/
protected $redirect = '/dashboard';
Or, if you would like to redirect users to a named route, you may define a $redirectRoute
property instead:
/**
* The route that users should be redirected to if validation fails.
*
* @var string
*/
protected $redirectRoute = 'dashboard';
Authorizing Form Requests
The form request class also contains an authorize
method. Within this method, you may determine if the authenticated user actually has the authority to update a given resource. For example, you may determine if a user actually owns a blog comment they are attempting to update. Most likely, you will interact with your authorization gates and policies within this method:
use App\Models\Comment;
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$comment = Comment::find($this->route('comment'));
return $comment && $this->user()->can('update', $comment);
}
Since all form requests extend the base Laravel request class, we may use the user
method to access the currently authenticated user. Also, note the call to the route
method in the example above. This method grants you access to the URI parameters defined on the route being called, such as the {comment}
parameter in the example below:
Route::post('/comment/{comment}');
Therefore, if your application is taking advantage of route model binding, your code may be made even more succinct by accessing the resolved model as a property of the request:
return $this->user()->can('update', $this->comment);
If the authorize
method returns false
, an HTTP response with a 403 status code will automatically be returned and your controller method will not execute.
If you plan to handle authorization logic for the request in another part of your application, you may simply return true
from the authorize
method:
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
You may type-hint any dependencies you need within the authorize
method's signature. They will automatically be resolved via the Laravel service container.
Customizing The Error Messages
You may customize the error messages used by the form request by overriding the messages
method. This method should return an array of attribute / rule pairs and their corresponding error messages:
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages()
{
return [
'title.required' => 'A title is required',
'body.required' => 'A message is required',
];
}
Customizing The Validation Attributes
Many of Laravel's built-in validation rule error messages contain an :attribute
placeholder. If you would like the :attribute
placeholder of your validation message to be replaced with a custom attribute name, you may specify the custom names by overriding the attributes
method. This method should return an array of attribute / name pairs:
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'email' => 'email address',
];
}
Preparing Input For Validation
If you need to prepare or sanitize any data from the request before you apply your validation rules, you may use the prepareForValidation
method:
use Illuminate\Support\Str;
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug($this->slug),
]);
}
Manually Creating Validators
If you do not want to use the validate
method on the request, you may create a validator instance manually using the Validator
facade. The make
method on the facade generates a new validator instance:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class PostController extends Controller
{
/**
* Store a new blog post.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Retrieve the validated input...
$validated = $validator->validated();
// Retrieve a portion of the validated input...
$validated = $validator->safe()->only(['name', 'email']);
$validated = $validator->safe()->except(['name', 'email']);
// Store the blog post...
}
}
The first argument passed to the make
method is the data under validation. The second argument is an array of the validation rules that should be applied to the data.
After determining whether the request validation failed, you may use the withErrors
method to flash the error messages to the session. When using this method, the $errors
variable will automatically be shared with your views after redirection, allowing you to easily display them back to the user. The withErrors
method accepts a validator, a MessageBag
, or a PHP array
.
Stopping On First Validation Failure
The stopOnFirstFailure
method will inform the validator that it should stop validating all attributes once a single validation failure has occurred:
if ($validator->stopOnFirstFailure()->fails()) {
// ...
}
Automatic Redirection
If you would like to create a validator instance manually but still take advantage of the automatic redirection offered by the HTTP request's validate
method, you may call the validate
method on an existing validator instance. If validation fails, the user will automatically be redirected or, in the case of an XHR request, a JSON response will be returned:
Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
])->validate();
You may use the validateWithBag
method to store the error messages in a named error bag if validation fails:
Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
])->validateWithBag('post');
Named Error Bags
If you have multiple forms on a single page, you may wish to name the MessageBag
containing the validation errors, allowing you to retrieve the error messages for a specific form. To achieve this, pass a name as the second argument to withErrors
:
return redirect('register')->withErrors($validator, 'login');
You may then access the named MessageBag
instance from the $errors
variable:
{{ $errors->login->first('email') }}