cavalheri / laravel-fuzzy-validation
Fuzzy validation rules for Laravel with approximate string matching support.
Package info
github.com/LucasCavalheri/laravel-fuzzy-validation
pkg:composer/cavalheri/laravel-fuzzy-validation
Requires
- php: ^8.3
- illuminate/contracts: ^13.0
- illuminate/database: ^13.0
- illuminate/support: ^13.0
- illuminate/validation: ^13.0
Requires (Dev)
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
This package is auto-updated.
Last update: 2026-05-25 19:31:54 UTC
README
Fuzzy validation rules for Laravel with approximate string matching support. Prevent near-duplicate entries such as The Matrix vs Th3 M4tr1x or Café vs Cafe.
Requirements
- PHP 8.3+
- Laravel 13+
Installation
composer require cavalheri/laravel-fuzzy-validation
The package supports auto-discovery. Publish the configuration file when needed:
php artisan vendor:publish --tag=fuzzy-validation-config
Quick Start
Fluent Rule API
use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; $validator = Validator::make($request->all(), [ 'title' => [ 'required', Rule::fuzzyUnique('movies', 'title') ->threshold(85) ->ignore($movie?->id) ->normalize(), ], ]);
String Syntax
'title' => 'required|fuzzy_unique:movies,title'
You can pass additional parameters:
'title' => 'required|fuzzy_unique:movies,title,90,normalize,driver=jaro_winkler,ignore=1'
Configuration
// config/fuzzy-validation.php return [ 'driver' => env('FUZZY_VALIDATION_DRIVER', 'similar_text'), 'threshold' => (int) env('FUZZY_VALIDATION_THRESHOLD', 85), 'normalization' => [ 'lowercase' => true, 'trim' => true, 'collapse_spaces' => true, 'remove_accents' => true, 'leetspeak' => false, ], 'messages' => [ 'fuzzy_unique' => 'The :attribute is too similar to an existing value.', ], ];
Features
Similarity Drivers
| Driver | Description |
|---|---|
similar_text |
PHP native similar_text() (default) |
levenshtein |
Levenshtein distance converted to percentage |
jaro_winkler |
Jaro-Winkler algorithm implementation |
Select a driver globally or per rule:
Rule::fuzzyUnique('movies', 'title')->driver('jaro_winkler')
Normalization Pipeline
Enable normalization on a rule:
Rule::fuzzyUnique('movies', 'title')->normalize()
Available normalizers:
- lowercase
- trim
- collapse spaces
- remove accents
- optional leetspeak (
4→a,3→e,1→i,0→o,5→s,7→t)
Enable leetspeak normalization:
Rule::fuzzyUnique('movies', 'title')->normalize()->leetspeak()
Override defaults per rule:
Rule::fuzzyUnique('movies', 'title')->normalize([ 'remove_accents' => false, 'leetspeak' => true, ])
Threshold
Similarity is measured from 0 to 100. Higher values require closer matches.
Rule::fuzzyUnique('movies', 'title')->threshold(90)
Ignore Records
Works like Laravel's built-in unique rule:
Rule::fuzzyUnique('movies', 'title')->ignore($movie->id) Rule::fuzzyUnique('movies', 'title')->ignore($movie) Rule::fuzzyUnique('movies', 'title')->ignore($uuid, 'uuid')
Additional Constraints
Rule::fuzzyUnique('movies', 'title')->where('published', true) Rule::fuzzyUnique('movies', 'title')->where(function ($query) { $query->where('tenant_id', tenant('id')); })
Custom Similarity Drivers
Implement the SimilarityDriver contract:
use Cavalheri\LaravelFuzzyValidation\Contracts\SimilarityDriver; final class MyDriver implements SimilarityDriver { public function compare(string $first, string $second): float { // Return similarity percentage (0-100) } }
Register the driver:
use Cavalheri\LaravelFuzzyValidation\Similarity\SimilarityManager; app(SimilarityManager::class)->extend('my_driver', MyDriver::class);
Use it in validation:
Rule::fuzzyUnique('movies', 'title')->driver('my_driver')
Database Support
The package works with any database supported by Laravel's query builder:
- MySQL
- PostgreSQL
- SQLite
Validation Message
Default message:
The :attribute is too similar to an existing value.
Customize via config or language files:
'fuzzy-validation.messages.fuzzy_unique' => 'This title is too close to an existing movie.',
Testing
composer test
Changelog
See CHANGELOG.md.
License
The MIT License (MIT). See LICENSE for details.