Laravel ships with a rich set of built-in validation rules, but you will sometimes need validation logic specific to your application. Custom validation rules let you define reusable validation logic as a class or closure and use it exactly like a standard rule.There are two main ways to define a custom rule:
Rule objects — highly reusable and easy to test in isolation
Call $fail more than once to report multiple errors for a single field.
public function validate(string $attribute, mixed $value, Closure $fail): void{ if (! is_string($value)) { $fail('The :attribute must be a string.'); return; } if (strlen($value) < 8) { $fail('The :attribute must be at least 8 characters.'); } if (! preg_match('/[A-Z]/', $value)) { $fail('The :attribute must contain at least one uppercase letter.'); }}
For simple rules you only need once, define the logic inline as a closure instead of creating a class.
use Illuminate\Support\Facades\Validator;use Closure;$validator = Validator::make($request->all(), [ 'title' => [ 'required', 'max:255', function (string $attribute, mixed $value, Closure $fail) { if ($value === 'foo') { $fail("The {$attribute} is invalid."); } }, ],]);
Closure rules are convenient for one-off logic, but they are harder to reuse and test independently. If the same rule appears in more than one place, extract it into a rule object.
By default, custom rules are skipped when the field is missing or empty. To run the rule regardless, add the --implicit flag when generating the class.
The generated class implements ImplicitRule. This interface has no additional methods — it is a marker interface that signals Laravel to run the rule on empty values too.
<?phpnamespace App\Rules;use Closure;use Illuminate\Contracts\Validation\ImplicitRule;use Illuminate\Contracts\Validation\ValidationRule;class RequiredWhenSubscribed implements ValidationRule, ImplicitRule{ public function validate(string $attribute, mixed $value, Closure $fail): void { if (empty($value)) { $fail('The :attribute is required for subscribers.'); return; } // Additional checks... }}
ImplicitRule only tells Laravel to run the rule on empty values. Whether empty values actually fail is still determined by your validate method implementation.
Validator::extend() lets you register a rule that can be referenced as a plain string (e.g. 'rule_name'). Register it in the boot() method of AppServiceProvider.
<?phpnamespace App\Providers;use Illuminate\Support\Facades\Validator;use Illuminate\Support\ServiceProvider;class AppServiceProvider extends ServiceProvider{ public function boot(): void { Validator::extend('strong_password', function (string $attribute, mixed $value, array $parameters): bool { return strlen($value) >= 8 && preg_match('/[A-Z]/', $value) && preg_match('/[0-9]/', $value); }); Validator::replacer('strong_password', function (string $message, string $attribute): string { return "The {$attribute} must be at least 8 characters and contain an uppercase letter and a number."; }); }}