DEV Community

Andres Daza
Andres Daza

Posted on

Avoid the N+1 Problem in Laravel Validations

The Hidden Problem Behind Massive Validations

When we validate arrays of data in Laravel using rules like unique, the framework executes one query per element in the array.

For example, if you validate a list of emails using the rule unique:users,email, Laravel will run a query for each email.

This results in what’s known as the N+1 problem, a classic performance bottleneck in database operations.

What Is the N+1 Problem in Validations?

Here’s what happens:

  • You have an array with N elements.
  • Laravel executes N SQL queries to validate each one.
  • As the array grows, the number of queries grows too.

This might not be noticeable for small datasets, but in massive operations (imports, APIs, data migrations), it quickly becomes a serious performance issue.

The Solution

Batch Validations with laravel-batch-validation

dazza-dev/laravel-batch-validation solves this problem by optimizing how Laravel handles database-dependent validation rules.

Instead of validating each element individually, this package groups validations and performs a single WHERE IN query per batch.

This means:

  • Hundreds of queries become just one or a few.
  • You keep Laravel’s native validation syntax.
  • You gain massive performance improvements with zero code complexity.

Installation

composer require dazza-dev/laravel-batch-validation
Enter fullscreen mode Exit fullscreen mode

Laravel will automatically detect the package thanks to auto-discovery,
so you don’t need to register anything manually.

Practical Example: Validating Arrays Without N+1

Imagine you receive an array of contacts to validate:

use Illuminate\Support\Facades\Validator;

$data = [
    ['name' => 'User 1', 'email' => 'user1@example.com'],
    ['name' => 'User 2', 'email' => 'user2@example.com'],
    ['name' => 'User 3', 'email' => 'user3@example.com'],
];

// Create the Validator instance
$validator = Validator::make($data, [
    '*.name' => 'required',
    '*.email' => 'email:strict|unique:contacts,email',
]);

// Validate in batches (this prevents the N+1 problem)
$validator->validateInBatches();

// Validation fails
if ($validator->fails()) {
    throw new \Exception(json_encode($validator->errors()->messages()));
}
Enter fullscreen mode Exit fullscreen mode

With this approach, instead of executing one query per email,
the package groups all values and runs a single WHERE IN query.

Control the Batch Size

In some cases, you may want to adjust the batch size to balance performance and memory usage.

You can do this by passing the batchSize parameter to validateInBatches:

$validator->validateInBatches(batchSize: 20);
Enter fullscreen mode Exit fullscreen mode

This means the validator will process the data in groups of 20 records at a time perfect for handling very large datasets efficiently.

Integration with FormRequests

The package also integrates seamlessly with Laravel’s FormRequest classes, keeping your validation flow clean and native.

For example:

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Validator;

class StoreContactRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            '*.name' => 'required',
            '*.email' => 'email:strict|unique:contacts,email',
        ];
    }

    public function withValidator(Validator $validator)
    {
        if (method_exists($validator, 'validateInBatches')) {
            $validator->validateInBatches(batchSize: 100);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This ensures that even your FormRequest validations are executed in batches, without changing any of your existing rules.

Result

Before
Before

After
After

Conclusion

dazza-dev/laravel-batch-validation is a smart and transparent optimization for Laravel projects that need to validate large datasets.

Fewer queries. Faster validations. Same Laravel syntax.

A must-have tool for imports, massive APIs, and large-scale data validations.

Top comments (0)