Skip to content

Laravel Custom Validation Rules: A Practical Guide with Real Examples

Unlock Laravel 12’s hidden validation power! Learn to create custom validation rules with step-by-step examples, boost security, and write cleaner code. Perfect for developers aiming to master Laravel’s advanced features in 2025.

Dasun Kalupahana 3 min read 886 views 0 comments

Are you tired of complex validation logic cluttering your Laravel controllers? Do you find yourself repeating the same validation patterns across multiple projects? Custom validation rules might be the solution you’ve been searching for!

In this comprehensive guide, we’ll walk through creating custom validation rules in Laravel that will help you write cleaner, more maintainable code while handling complex validation scenarios with ease.


Why Custom Validation Rules Matter

Laravel’s built-in validation rules are powerful, but they can’t cover every possible scenario your application might need. Custom validation rules allow you to:

  • Encapsulate complex business logic
  • Create reusable validation components
  • Improve code readability
  • Maintain consistency across your application
  • Simplify testing of validation logic

Step-by-Step: Creating a Phone Number Validation Rule

Let’s create a practical custom validation rule that ensures users enter a valid phone number format.

Step 1: Generate the Validation Rule

Use Laravel’s Artisan command to create your custom rule:

php artisan make:rule PhoneNumberRule

This creates a new file at app/Rules/PhoneNumberRule.php.

Step 2: Implement the Validation Logic

Open the generated file and implement your validation logic:

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class PhoneNumberRule implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // Basic phone number validation (adjust regex for your needs)
        $pattern = '/^\+?[1-9]\d{1,14}$/';
        
        if (!preg_match($pattern, $value)) {
            $fail('The :attribute must be a valid phone number format.');
        }
    }
}

Step 3: Create Routes

Set up routes to handle both the form display and form submission:

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

Route::prefix('users')->group(function () {
   Route::get('/', [UserController::class, 'create']);
   Route::post('create', [UserController::class, 'store'])->name('users.store');
});

Step 4: Build the Controller

Create a controller to handle the validation logic:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\View\View;
use Illuminate\Http\RedirectResponse;
use App\Rules\PhoneNumberRule;

class UserController extends Controller
{
    public function create(): View
    {
        return view('users.create-user');
    }
        
    public function store(Request $request): RedirectResponse
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|email|max:255|string|unique:users,email',
            'phone_number' => [
                'required',
                new PhoneNumberRule()
            ]
        ]);

        // Process valid data here (e.g., save to database)
        
        return back()->with('success', 'User created successfully!');
    }
}

Step 5: Design the Form View

Create a user-friendly form with proper error handling:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>User Creation</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container py-5">
        <div class="row justify-content-center">
            <div class="col-md-6">
                <div class="card shadow">
                    <div class="card-header bg-primary text-white">
                        <h2 class="h4 mb-0">User Creation Form</h2>
                    </div>
                    <div class="card-body">
                        @if(session('success'))
                            <div class="alert alert-success">
                                {{ session('success') }}
                            </div>
                        @endif

                        <form method="POST" action="{{ route('users.store') }}">
                            @csrf
                            <div class="mb-3">
                                <label for="name" class="form-label">Name</label>
                                <input type="text" 
                                       class="form-control @error('name') is-invalid @enderror" 
                                       id="name" 
                                       name="name" 
                                       value="{{ old('name') }}" 
                                       required>
                                @error('name')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="email" class="form-label">Email</label>
                                <input type="email" 
                                       class="form-control @error('email') is-invalid @enderror" 
                                       id="email" 
                                       name="email" 
                                       value="{{ old('email') }}" 
                                       required>
                                @error('name')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                            </div>
                            <div class="mb-3">
                                <label for="phone_number" class="form-label">Phone Number</label>
                                <input type="text" 
                                       class="form-control @error('phone_number') is-invalid @enderror" 
                                       id="phone_number" 
                                       name="phone_number" 
                                       value="{{ old('phone_number') }}" 
                                       placeholder="+1234567890"
                                       required>
                                @error('phone_number')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                                <div class="form-text">Enter your phone number in international format (e.g., +1234567890)</div>
                            </div>

                            <button type="submit" class="btn btn-primary w-100">Create</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Testing Your Custom Validation Rule

Run your Laravel development server:

php artisan serve

Visit http://localhost:8000/users in your browser to test the form. Try submitting both valid and invalid phone numbers to see the custom validation rule in action.


Expanding Your Custom Validation Toolkit

Now that you’ve mastered the basics, consider creating other useful custom validation rules:

  1. Email Domain Validation: Ensure emails are from specific domains
  2. Username Availability: Check if a username is already taken
  3. Password Strength: Enforce strong password requirements
  4. File Type Validation: Restrict uploads to specific file types
  5. Business Logic Rules: Validate according to your specific business requirements

Best Practices for Custom Validation Rules

  1. Keep Rules Focused: Each rule should handle one specific validation concern
  2. Provide Clear Error Messages: Help users understand what went wrong
  3. Test Thoroughly: Write tests for both valid and invalid inputs
  4. Reuse When Possible: Create rules that can be used across multiple parts of your application
  5. Follow Naming Conventions: Use descriptive names that indicate the rule’s purpose

Conclusion

Custom validation rules are a powerful feature in Laravel that can significantly improve your development workflow and application quality. By encapsulating validation logic in dedicated rule classes, you create more maintainable, testable, and reusable code.

The phone number validation example we’ve created demonstrates how easy it is to implement custom validation rules that handle specific business requirements. As you continue developing with Laravel, consider building a library of custom validation rules that you can reuse across projects.

Remember, the goal of validation is not just to prevent invalid data but to provide clear guidance to users and maintain data integrity throughout your application. With custom validation rules, you have the flexibility to implement exactly the validation logic your application needs.

If you found this helpful, feel free to share or drop a comment. Happy coding with Laravel! 🧱✨

Free resources for this topic

Code snippets and fixes for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Free download: Laravel interview questions (PDF)

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 0

Be kind. Comments with links are reviewed before they appear.

No comments yet

Be the first to share your thoughts.

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.