x-laravel / fuzzy-similarity
Fuzzy (string-based) similarity for Laravel Eloquent models.
Requires
- php: ^8.4
- ext-intl: *
- ext-mbstring: *
- illuminate/database: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-26 17:07:53 UTC
README
Fuzzy (string-based) similarity for Laravel Eloquent models: compare two records, or rank a collection against a query, by how closely their text matches.
It measures how the text is written, not what it means. For meaning-based (vector) similarity see x-laravel/embedding; the two packages can be used on the same model side by side.
How It Works
- Add the
FuzzyComparabletrait and declare the compared columns with#[FuzzyOn] - Define one or more named slots per model; different models are compared through the same slot name (one model's
nameagainst another'stitle) - Both sides are normalized first — by default to lower-case ASCII, so
Crème Brûléeequalscreme-bruleeandSão PauloequalsSao Paulo - Scores range from
0.0to100.0; the algorithm is configurable per call or globally
Requirements
- PHP ^8.4 with
intlandmbstring - Laravel ^12.0 | ^13.0
Installation
composer require x-laravel/fuzzy-similarity
Optionally publish the config file:
php artisan vendor:publish --tag=fuzzy-similarity-config
Setup
use Illuminate\Database\Eloquent\Model; use XLaravel\FuzzySimilarity\Attributes\FuzzyOn; use XLaravel\FuzzySimilarity\Concerns\FuzzyComparable; use XLaravel\FuzzySimilarity\Contracts\HasFuzzySimilarity; #[FuzzyOn('name')] #[FuzzyOn(['name', 'city'], slot: 'location')] class Company extends Model implements HasFuzzySimilarity { use FuzzyComparable; } #[FuzzyOn('title')] class Article extends Model implements HasFuzzySimilarity { use FuzzyComparable; }
A slot's columns are joined with a space; blank values are skipped. Override toFuzzyText(string $slot) to build the text yourself.
Usage
Compare two records
$company->fuzzySimilarityTo($otherCompany); // default slot $company->fuzzySimilarityTo($otherCompany, slot: 'location'); $company->fuzzySimilarityTo($article); // Company name vs Article title $company->fuzzySimilarityTo('Acme Corporation'); // plain string $company->fuzzySimilarityTo('Corporation Acme', algorithm: 'token_set');
Rank a collection
$ranked = Company::rankByFuzzy($candidates, 'Acme Corporation', limit: 20, threshold: 40.0); $ranked->first()->fuzzy_score; // 0.0 – 100.0
The query may also be a model. Each ranked model gets a fuzzy_score attribute; ranking happens in PHP, so pass an already narrowed-down collection.
Compare strings directly
use XLaravel\FuzzySimilarity\FuzzySimilarity; app(FuzzySimilarity::class)->compare('São Paulo', 'sao-paulo'); // 100.0
Algorithms
| Key | Behaviour |
|---|---|
similar_text (default) |
PHP's similar_text() percentage, computed over characters instead of bytes. Gives the same result as the native function on ASCII input. |
token_set |
Ignores word order and repeated words. A string whose words all appear in the other scores 100 (Acme Corporation vs Acme Corporation International). |
Add your own by implementing XLaravel\FuzzySimilarity\Contracts\Algorithm and registering it under algorithms in the config, or pass its class name as algorithm:.
Normalizers
| Class | Behaviour |
|---|---|
AsciiNormalizer (default) |
Transliterates to lower-case ASCII (é → e, ß → ss, ø → o), turns - _ . · and dashes into spaces, drops invisible characters (zero-width space, BOM, word joiner…), collapses whitespace. |
LowercaseNormalizer |
Lower-cases and collapses whitespace only. |
Set normalizer in the config to any class implementing XLaravel\FuzzySimilarity\Contracts\Normalizer.
AsciiNormalizer keeps the last 10,000 results in a per-process cache, so a name compared against thousands of candidates is folded once, and long-running processes (Octane, queue workers) reuse results across requests. When the cache is full the oldest entry is dropped. Call AsciiNormalizer::flushCache() to empty it.
Testing
docker compose --profile php84 run --rm php84 docker compose --profile php85 run --rm php85
License
MIT