Looking to hire Laravel developers? Try LaraJobs

fuzzy-similarity maintained by x-laravel

Description
Fuzzy (string-based) similarity for Laravel Eloquent models.
Author
Last update
2026/09/26 19:04 (dev-master)
License
Links
Downloads
1

Comments
comments powered by Disqus

Fuzzy Similarity

Tests PHP Laravel License

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 FuzzyComparable trait 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 name against another's title)
  • Both sides are normalized first — by default to lower-case ASCII, so Crème Brûlée equals creme-brulee and São Paulo equals Sao Paulo
  • Scores range from 0.0 to 100.0; the algorithm is configurable per call or globally

Requirements

  • PHP ^8.4 with intl and mbstring
  • 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