• Fri, Mar 2026

Advanced Validation Rules in Laravel for Secure Forms

Advanced Validation Rules in Laravel for Secure Forms

In this in-depth tutorial, you will learn how to implement advanced validation rules in Laravel for secure forms. We’ll cover everything from custom rules, closures, and conditional validation to form requests and security best practices. By the end, you’ll have the skills to build robust, safe, and professional-grade Laravel applications.

Introduction

When working with user input, validation is one of the most critical steps in securing your Laravel application. Beginners often rely only on simple built-in rules like required, email, or min, but Laravel’s validation system is much more powerful. It allows you to create custom rules, use closures, validate arrays, apply conditional logic, and even build reusable form request classes.

In this article, we’ll take a professional deep dive into Laravel’s advanced validation rules and techniques. Each step will be backed with examples and clear explanations so you can confidently apply them in your real-world projects.

Getting Started with Laravel Validation

Basic Validation Refresher

Laravel provides the validate() method and the Validator facade to apply validation rules. Here’s a quick refresher:


// In a controller method
public function store(Request $request)
{
    $validated = $request->validate([
        'name'  => 'required|string|max:255',
        'email' => 'required|email|unique:users,email',
        'age'   => 'nullable|integer|min:18'
    ]);

    // If validation passes, continue
    User::create($validated);
}
      

This basic validation is helpful, but not enough for complex or security-critical applications. Let’s go deeper.

Advanced Built-In Validation Rules

1. Using Regular Expressions

You can enforce patterns using the regex rule:


$request->validate([
    'username' => ['required', 'regex:/^[a-zA-Z0-9_]{5,20}$/']
]);
      

This ensures only alphanumeric characters and underscores, with a length between 5–20.

2. Validating Arrays and Nested Data

Laravel makes it easy to validate arrays, such as multiple phone numbers:


$request->validate([
    'phones'   => 'required|array|min:1',
    'phones.*' => 'regex:/^[0-9]{10}$/'
]);
      

3. Confirmed and Different


$request->validate([
    'password' => 'required|min:8|confirmed',
    'username' => 'required|different:email'
]);
      

4. Date and Time Validations


$request->validate([
    'start_date' => 'required|date|after_or_equal:today',
    'end_date'   => 'required|date|after:start_date'
]);
      

Custom Validation Rules

Creating a Custom Rule Class

You can generate a custom validation rule using Artisan:


php artisan make:rule StrongPassword
      

This creates app/Rules/StrongPassword.php where you can define your logic:


namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class StrongPassword implements Rule
{
    public function passes($attribute, $value)
    {
        return preg_match('/^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/', $value);
    }

    public function message()
    {
        return 'The :attribute must be at least 8 characters long and include one uppercase letter, one number, and one special character.';
    }
}
      

Now use it in your controller:


use App\Rules\StrongPassword;

$request->validate([
    'password' => ['required', new StrongPassword]
]);
      

Using Closure-Based Custom Rules


use Illuminate\Support\Facades\Validator;

$validator = Validator::make($request->all(), [
    'code' => [
        'required',
        function ($attribute, $value, $fail) {
            if ($value !== 'SECRET123') {
                $fail($attribute.' is invalid.');
            }
        },
    ],
]);
      

Conditional Validation

Sometimes rules depend on other fields. Laravel offers several techniques.

1. Using required_if and required_unless


$request->validate([
    'company_name' => 'required_if:is_employed,true',
    'age'          => 'required_unless:is_minor,true'
]);
      

2. Using sometimes()


$validator = Validator::make($request->all(), [
    'phone' => 'nullable|regex:/^[0-9]{10}$/'
]);

$validator->sometimes('phone', 'required', function ($input) {
    return $input->wants_contact === true;
});
      

3. Conditional Rules with bail

The bail rule stops on the first failure:


$request->validate([
    'username' => 'bail|required|min:5|alpha_num|unique:users,username'
]);
      

Form Requests for Cleaner Code

Form Requests encapsulate validation logic in dedicated classes, improving code maintainability.


php artisan make:request RegisterUserRequest
      

Inside app/Http/Requests/RegisterUserRequest.php:


public function rules()
{
    return [
        'name'     => 'required|string|max:255',
        'email'    => 'required|email|unique:users,email',
        'password' => ['required', new StrongPassword],
    ];
}
      

In the controller:


public function store(RegisterUserRequest $request)
{
    User::create($request->validated());
}
      

Validating Files and Images

Laravel can validate file uploads with ease:


$request->validate([
    'avatar' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048|dimensions:min_width=100,min_height=100'
]);
      

This ensures only images under 2MB with minimum dimensions of 100×100 are accepted.

Localization of Validation Messages

You can customize error messages in resources/lang/en/validation.php. For example:


'custom' => [
    'username' => [
        'regex' => 'Username may only contain letters, numbers, and underscores.',
    ],
],
      

Best Practices for Secure Validation

  • Always validate both client-side and server-side.
  • Use custom rules for domain-specific security needs.
  • Never trust hidden inputs; validate all incoming data.
  • Prefer Form Requests for reusable, clean code.
  • Use bail to reduce unnecessary processing.
  • Keep error messages user-friendly but not overly revealing.

Comparison Table: Validation Methods

The table below summarizes different ways to apply validation in Laravel:

MethodUse CaseExample
Inline ValidationSimple one-off validations$request->validate([...])
Validator FacadeMore control and conditional logicValidator::make(...)
Form RequestReusable, structured validationRegisterUserRequest

Minimal but production-ready example

Let's create a minimal but complete Laravel example that demonstrates advanced validation rules in one flow. This example will include:

  • A form for user registration.
  • Controller method with advanced validation rules.
  • A custom rule (StrongPassword).
  • Form Request class for clean validation.
  • Example of conditional validation.

1. Create the Route

// routes/web.php
use App\Http\Controllers\RegisterController;

Route::get('/register', [RegisterController::class, 'create']);
Route::post('/register', [RegisterController::class, 'store']);

2. Create the Controller

// app/Http/Controllers/RegisterController.php
namespace App\Http\Controllers;

use App\Http\Requests\RegisterUserRequest;
use App\Models\User;

class RegisterController extends Controller
{
    public function create()
    {
        return view('register');
    }

    public function store(RegisterUserRequest $request)
    {
        // Validation already handled by RegisterUserRequest

        User::create([
            'name'     => $request->name,
            'email'    => $request->email,
            'password' => bcrypt($request->password),
            'phone'    => $request->phone,
        ]);

        return redirect('/register')->with('success', 'User registered successfully!');
    }
}

3. Create the Custom Rule

php artisan make:rule StrongPassword
// app/Rules/StrongPassword.php
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class StrongPassword implements Rule
{
    public function passes($attribute, $value)
    {
        return preg_match('/^(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/', $value);
    }

    public function message()
    {
        return 'The :attribute must contain at least 8 characters, one uppercase letter, one number, and one special character.';
    }
}

4. Create the Form Request

php artisan make:request RegisterUserRequest
// app/Http/Requests/RegisterUserRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use App\Rules\StrongPassword;

class RegisterUserRequest extends FormRequest
{
    public function authorize()
    {
        return true; // allow all for demo
    }

    public function rules()
    {
        return [
            'name'     => 'required|string|max:100',
            'email'    => 'required|email|unique:users,email',
            'password' => ['required', new StrongPassword, 'confirmed'],
            'phone'    => 'nullable|regex:/^[0-9]{10}$/',
            'company'  => 'required_if:is_employed,true',
            'is_employed' => 'boolean',
        ];
    }

    public function messages()
    {
        return [
            'company.required_if' => 'Company name is required when employed.',
            'phone.regex'         => 'Phone number must be exactly 10 digits.',
        ];
    }
}

5. Create the Blade View

<!-- resources/views/register.blade.php -->
<!DOCTYPE html>
<html>
<head>
  <title>Advanced Validation Example</title>
</head>
<body>
  <h1>User Registration</h1>

  @if(session('success'))
    <p >{{ session('success') }}</p>
  @endif

  @if ($errors->any())
    <div >
      <ul>
        @foreach ($errors->all() as $error)
          <li>{{ $error }}</li>
        @endforeach
      </ul>
    </div>
  @endif

  <form method="POST" action="/register">
    @csrf
    <label>Name:</label>
    <input type="text" name="name" value="{{ old('name') }}"><br><br>

    <label>Email:</label>
    <input type="email" name="email" value="{{ old('email') }}"><br><br>

    <label>Password:</label>
    <input type="password" name="password"><br><br>

    <label>Confirm Password:</label>
    <input type="password" name="password_confirmation"><br><br>

    <label>Phone:</label>
    <input type="text" name="phone" value="{{ old('phone') }}"><br><br>

    <label>Are you employed?</label>
    <input type="checkbox" name="is_employed" value="true" {{ old('is_employed') ? 'checked' : '' }}><br><br>

    <label>Company Name (required if employed):</label>
    <input type="text" name="company" value="{{ old('company') }}"><br><br>

    <button type="submit">Register</button>
  </form>
</body>
</html>

Conclusion

Laravel’s validation system goes far beyond the basics. With advanced rules, custom logic, conditional validation, and Form Requests, you can build secure, scalable, and user-friendly applications. The key takeaway is: never compromise on validation. Treat every piece of user input as potentially harmful, and leverage Laravel’s powerful tools to protect your application.

Now, practice applying these techniques in your projects. Start small by adding a custom rule, then move toward full-fledged Form Requests with complex validation logic. Over time, you’ll master secure form handling in Laravel.

This website uses cookies to enhance your browsing experience. By continuing to use this site, you consent to the use of cookies. Please review our Privacy Policy for more information on how we handle your data. Cookie Policy