Extending/Overriding Laravel Validator Class

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Deep Dive: Extending Laravel's Core Validator for Custom Validation Logic

As developers working with the Laravel ecosystem, we often find ourselves needing to adapt or extend core framework components to fit specific project requirements. While Laravel is designed to be highly cohesive, sometimes the built-in mechanisms don't perfectly align with complex business logic. A prime example of this is customizing how validation processes execute.

In recent updates, features like stopOnFirstFailure have introduced powerful control over the validation flow. If you are working on an older version of Laravel, or need a highly specific, customized behavior that isn't exposed in the default API, extending core classes like Illuminate\Validation\Validator is a powerful, albeit advanced, technique.

This post will walk you through how to extend the base Validator class in Laravel 7 to introduce custom functionality, demonstrating the necessary steps involving inheritance, service container registration, and dependency injection.

The Challenge: Customizing Core Behavior

The goal here is not to rewrite Laravel, but to layer new behavior onto an existing component. The core challenge lies in overriding or adding methods within Validator.php—specifically modifying how validation rules are processed in the passes() method. To achieve this cleanly and maintain framework standards, we need a strategy that allows our custom class to replace the default validator when requested by the container.

We aim to implement a feature similar to Laravel 8's $stopOnFirstFailure, requiring us to:

  1. Define a new protected property ($stopOnFirstFailure).
  2. Implement a method to set this state (stopOnFirstFailure()).
  3. Modify the core logic in passes() to respect this state and break the validation loop early.

Implementation Strategy: Extension via Service Container

Directly modifying framework files is strictly discouraged, as updates will overwrite your changes. The correct approach in Laravel is to leverage the Service Container to swap out the default implementation with our custom one. This technique relies on PHP's object-oriented features (inheritance) combined with Laravel's service binding capabilities.

We will achieve this by creating a custom validator class and registering it as an alternative implementation via a custom Factory, all managed through a Service Provider. This adheres to the principles of dependency inversion and keeps our extension framework-agnostic.

Step 1: Creating the Custom Validator Class

We extend the original Validator class to introduce our new functionality. This is where we define the state and the methods that control the validation flow.

namespace App\CustomClass;

use Illuminate\Validation\Validator;
use Illuminate\Support\MessageBag;

class CustomValidator extends Validator
{
    /**
     * Indicates if the validator should stop on the first rule failure.
     *
     * @var bool
     */
    protected $stopOnFirstFailure = true; // Defaulting to true for demonstration

    /**
     * Instruct the validator to stop validating after the first rule failure.
     *
     * @param  bool  $stopOnFirstFailure
     * @return $this
     */
    public function stopOnFirstFailure($stopOnFirstFailure = true)
    {
        $this->stopOnFirstFailure = $stopOnFirstFailure;

        return $this;
    }

    /**
     * Determine if the data passes the validation rules, incorporating our custom logic.
     *
     * @return bool
     */
    public function passes()
    {
        $this->messages = new MessageBag;
        [$this->distinctValues, $this->failedRules] = [[], []];

        // We'll spin through each rule, validating the attributes attached to that
        foreach ($this->rules as $attribute => $rules) {
            if ($this->shouldBeExcluded($attribute)) {
                $this->removeAttribute($attribute);
                continue;
            }

            // *** Custom Logic Implementation ***
            if ($this->stopOnFirstFailure && $this->messages->isNotEmpty()) {
                break; // Stop immediately if we found a failure and are set to stop.
            }
            // **********************************

            foreach ($rules as $rule) {
                $this->validateAttribute($attribute, $rule);

                if ($this->shouldBeExcluded($attribute)) {
                    $this->removeAttribute($attribute);
                    break;
                }

                if ($this->shouldStopValidating($attribute)) {
                    break;
                }
            }
        }
        return parent::passes();
    }
}

Step 2: Registering the Extension via Service Provider

To make Laravel use CustomValidator instead of the default one, we must register it in our service container. We achieve this by extending the validator binding using $this->app->extend(). This ensures that any part of the application requesting an instance of Validator will receive our custom implementation.

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\CustomClass\ValidatorFactory;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        // Extend the 'validator' binding to use our custom factory/