Profile    Mohammed Shiroz Status   Loading  
Logo
Share This
Back to blog
Filter by:
Tags
//Article title

Laravel Validation Rules You're Probably Not Using (But Should Be)

About Post

Most Laravel validation I review looks the same: required, string, max:255, email, and then a pile of manual checks in the controller for everything else. "If the type is periodic, make sure there's no end date." "Check that this unit actually belongs to this property." "Make sure the email is unique, except for this user."

Laravel already has rules for almost all of that. They're in the docs, in a long alphabetical list that nobody reads from top to bottom. So here are the ones I reach for most, each with the problem it solves, all for Laravel 12.

Unique, except for me

The problem: on an "edit profile" form, unique:users,email fails because the user's own email is already in the table.

use Illuminate\Validation\Rule;

'email' => [
    'required',
    'email',
    Rule::unique('users')->ignore($this->user()),
],

ignore() accepts the model or its id. Two things worth knowing: never pass a value straight from the request into ignore() (the docs warn about this; use the model you loaded), and if the model uses soft deletes, add ->whereNull('deleted_at') so a deleted user's email doesn't block a new account forever.

Exists, but only where it should

The problem: exists:units,id checks that the unit exists. It doesn't check that it belongs to the property the user is working on. That's not just a data bug, it's an authorization hole: send any id, attach any unit.

'unit_id' => [
    'required',
    Rule::exists('units', 'id')
        ->where('property_id', $this->route('property')->id)
        ->whereNull('archived_at'),
],

Scoping exists to the current parent (or the current team or tenant in a multi-tenant app) is one of the cheapest security wins in Laravel. Policies still matter, but this stops a whole class of "I changed the id in the request" tricks at the door.

sometimes: validate it only if it's sent

The problem: a PATCH endpoint where the client sends only the fields it wants to change. required would demand everything; dropping required would let someone send an empty name.

'name'  => ['sometimes', 'required', 'string', 'max:120'],
'phone' => ['sometimes', 'nullable', 'string', 'max:20'],

Read it as "if name is present, it must be filled in". Compare that with nullable, which means "present, but may be empty". Mixing up the two is behind a lot of "why did my API accept that?" moments.

bail: stop at the first failure

The problem: a field fails the cheap email check, and Laravel still runs the unique rule, which queries the database with a value you already know is garbage. The user also gets three error messages about one field.

'email' => ['bail', 'required', 'email', Rule::unique('users')],

bail works per field. If you want the whole request to stop at the first failing field, set protected $stopOnFirstFailure = true; on the form request.

Conditional rules: required_if, prohibited_if, exclude_if

The problem: rules that depend on another field. A fixed-term contract needs an end date. A periodic one must not have one. This logic usually ends up as if statements in the controller.

'type'     => ['required', Rule::in(['fixed', 'periodic'])],
'end_date' => [
    'nullable',
    'required_if:type,fixed',
    'prohibited_if:type,periodic',
    'date',
    'after:start_date',
],

prohibited_if is the underrated one. It turns "the client sent something that makes no sense" into a clear 422 error, instead of storing contradictory data. When you'd rather silently drop the field, use exclude_if:type,periodic: it's removed from validated() entirely. There are closure versions too, like Rule::requiredIf(fn () => $this->user()->isAdmin()), for conditions that aren't just "field equals value".

The Password rule

The problem: password rules copied between forms, slightly different each time.

use Illuminate\Validation\Rules\Password;

// In AppServiceProvider::boot()
Password::defaults(fn () => Password::min(10)
    ->letters()
    ->mixedCase()
    ->numbers()
    ->uncompromised());

// In any form request
'password' => ['required', 'confirmed', Password::defaults()],

Define the policy once, use it everywhere. uncompromised() checks the password against the Have I Been Pwned breach database using k-anonymity, so only the first five characters of the password's SHA-1 hash leave your server, never the password itself.

The enum rule

The problem: a status field validated with in:draft,active,ended, a list that drifts out of sync with the enum in your code.

'status' => [
    'required',
    Rule::enum(ContractStatus::class)
        ->only([ContractStatus::Draft, ContractStatus::Active]),
],

The enum is the single source of truth. only() and except() let you restrict the choice for a specific form: a user can create a draft or active contract, but "ended" only happens through the end-contract flow.

Three small ones that punch above their weight

  • 'items.*.amount' => ['required', 'decimal:0,2']: wildcard validation for array rows, and a money-friendly rule that limits decimal places.
  • 'emails.*' => ['distinct:ignore_case']: no duplicates inside the same submitted array.
  • 'meta' => ['array:source,campaign']: the array may only contain those keys, so nobody sneaks extra data into a JSON column.

A good rule of thumb: if your controller has an if that checks the shape or relationship of incoming data and returns an error, it probably belongs in the form request as a validation rule. Controllers should receive data that's already known to be valid.

The quick list

  • Rule::unique()->ignore() for edit forms.
  • Rule::exists()->where() to keep ids inside the right parent.
  • sometimes for partial updates.
  • bail to stop wasted checks.
  • required_if, prohibited_if, exclude_if for conditional fields.
  • Password::defaults() and Rule::enum() for one source of truth.

The full list is in the Laravel validation docs, and it's worth one slow read. Which validation rule did you discover embarrassingly late? Mine was prohibited_if.

Comments (0)
Leave your review

Thanks for your valuable comments. Your comments has been updated and appreciate your getting in touch...

01. About Shiroz

Mohammed Shiroz

Hi, I'm Mohammed Shiroz, a software engineer and AI enthusiast from Sri Lanka who turns ideas into intelligent, real-world solutions. With over 9 years of hands-on experience, I currently lead real estate ERP development at Kate Group, a...

03.My Projects

04. Categories

Ready To order Your Project ?

Get in Touch
Close