To create a custom validation rule in Laravel 13, run php artisan make:rule, implement the validate() method of the ValidationRule interface, call $fail() when the value is invalid, and add the rule object to your rules array. For one-off checks, a closure with the same signature works without a new class.
What do you need before you start?
- Laravel 13 on PHP 8.3 or newer
- A form request or controller that already validates input
How do you generate a custom rule class?
php artisan make:rule StrongUsernameThe command creates app/Rules/StrongUsername.php. The class implements Illuminate\Contracts\Validation\ValidationRule and has one method, validate(), which receives the attribute name, the value and a $fail closure.
How do you write the rule logic?
This example rejects usernames that are too short, start with a digit, or contain characters outside letters, numbers and underscores.
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class StrongUsername implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (! is_string($value) || strlen($value) < 4) {
$fail('The :attribute must be at least 4 characters.');
return;
}
if (ctype_digit($value[0])) {
$fail('The :attribute must not start with a number.');
}
if (! preg_match('/^[A-Za-z0-9_]+$/', $value)) {
$fail('The :attribute may only contain letters, numbers and underscores.');
}
}
}The :attribute placeholder is replaced with the field name. Each $fail() call adds one error message.
How do you use the rule in a request?
use App\Rules\StrongUsername;
$request->validate([
'username' => ['required', 'string', new StrongUsername],
]);In a form request, return the same array from rules(). Put cheap built-in rules such as required first so the custom class only runs on plausible input.
Can you use a closure instead of a class?
Yes. A closure rule has the same signature and suits logic you will not reuse.
use Closure;
$request->validate([
'coupon' => [
'required',
function (string $attribute, mixed $value, Closure $fail) {
if (! in_array(strtoupper($value), ['SPRING10', 'WELCOME5'], true)) {
$fail("The {$attribute} is not valid.");
}
},
],
]);How do you give a rule access to other fields?
Implement DataAwareRule to receive all validated data, or ValidatorAwareRule to receive the validator instance. The rule class gets a setData() or setValidator() method that Laravel calls before validation.
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\ValidationRule;
class DifferentFromEmail implements ValidationRule, DataAwareRule
{
protected array $data = [];
public function setData(array $data): static
{
$this->data = $data;
return $this;
}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (isset($this->data['email']) && $value === $this->data['email']) {
$fail('The :attribute must be different from the email.');
}
}
}How do you translate the error message?
Call translate() on the object returned by $fail() to use a language file key instead of a hard-coded string. Check the validation documentation for the exact placeholder and locale options.
$fail('validation.custom.username_weak')->translate();How do you verify it works?
Write a small feature test that posts bad and good values and asserts the session errors.
public function test_username_rule_rejects_leading_digit(): void
{
$this->post('/register', [
'username' => '1admin',
'email' => 'a@example.com',
'password' => 'password123',
])->assertSessionHasErrors('username');
}What are the common mistakes?
- Ignoring non-string input, which breaks
strlen()orpreg_match() - Putting heavy database queries in a rule that runs on every request
- Forgetting to
returnafter the first$fail()when later checks would error - Relying on a rule class for security without a database unique constraint
FAQ
Does a custom rule run when the field is empty?
By default, rules do not run for empty values unless the field is implicit (for example required). Combine your rule with required when the field is mandatory.
Where should I store custom rules?
The default is app/Rules, which make:rule creates for you.
Rule class or closure?
Use a class when you reuse or unit test the logic. Use a closure for a single form.



