Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Searchy Laravel Package

tom-lingham/searchy

Searchy is a Laravel package for fuzzy database searching. Query multiple columns with relevance scoring and drivers like simple, fuzzy, and levenshtein, returning the most relevant records instead of relying on plain exact LIKE matches.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and pin. composer require tom-lingham/searchy. The package is archived (last release 2020-05-08) and its illuminate/support constraint tops out around Laravel 7, so on Laravel 8+ expect to either use a fork, add a repositories vcs entry, or pass --ignore-platform-reqs. Verify the resolved constraint with composer show tom-lingham/searchy before writing any code against it.
  2. Publish the config. php artisan vendor:publish --provider="TomLingham\Searchy\SearchyServiceProvider" gives you config/searchy.php, which holds the default driver and the driver registry (fuzzy, simple, levenshtein).
  3. Read two things first: config/searchy.php (which driver is default and how drivers are keyed) and src/SearchDrivers/SimpleSearchDriver.php (the smallest driver — it shows exactly what SQL shape every driver produces). Everything else in the package is a variation on that one query-building method.
  4. First use case — fuzzy search over a table:
use TomLingham\Searchy\Facades\Searchy;

$results = Searchy::users('name')->query('jon')->get();

users is the table name, 'name' is the column list, query() is the user input, get() returns an Eloquent Collection where every model carries an extra relevance attribute. Render that attribute to show match quality, and sort by it if you need deterministic ordering.

Implementation Patterns

Pick the driver per query, not per app. The three bundled drivers trade accuracy for portability:

  • simple — straight LIKE '%term%' across the fields. Cheapest, works on every DB, no fuzzy tolerance.
  • fuzzy — splits the term on whitespace and scores each word per column, weighting prefix matches above mid-string matches. This is the default and the one you want for a search box.
  • levenshtein — typo tolerance via edit distance. Requires a LEVENSHTEIN SQL function to exist in your database; it is not shipped by the driver.
Searchy::driver('simple')->users('name', 'email')->query($term)->get();

Multi-field search is just variadic columns. Searchy::users('name', 'email', 'username') OR's the conditions and sums relevance. Keep the column list short — every extra column multiplies the generated SQL and the scan cost.

Constrain columns and rows before get(). The builder proxies to Eloquent, so select(), limit(), and where() chain naturally:

$results = Searchy::users('name', 'email')
    ->query($input)
    ->select('id', 'name', 'email')
    ->limit(25)
    ->get();

Confirm with ->getQuery()->toSql() that your select() didn't drop the relevance alias — if it did, drop back to *.

Wrap it in a repository method, never call the facade from a controller. The facade's magic __call maps the method name to a table, so a typo in Searchy::user('name') silently queries a non-existent table at runtime. A single UserSearch::run(string $term) method centralises the table/column list, gives you one place to sanitise input, and makes it swappable when you inevitably migrate off an archived package.

Ordering and pagination live outside Searchy. get() hands back a plain Collection, not a paginator. For paginated UIs, take getQuery() and paginate it yourself (verify the relevance alias survives paginate()), or apply ->limit() plus a manual offset. If you need eager loading, ->load('profile') on the returned collection — Searchy has no relationship awareness and cannot join for you.

Custom drivers are the extension point. Extend TomLingham\Searchy\SearchDrivers\BaseSearchDriver, override the query-string method (copy SimpleSearchDriver as the template), then register the class in config/searchy.php under drivers. That's how you swap LIKE for SOUNDEX, a prefix-only match, or a MATCH ... AGAINST full-text call.

Index for the pattern you actually use. LIKE 'term%' can use a B-tree index; LIKE '%term%' cannot. If your tables are large, write a custom driver that matches prefixes and let the fuzzy behaviour live in the fallback path.

Gotchas and Tips

  • Unmaintained and effectively EOL. No PHP 8.1+ or Laravel 9+ support in the tagged release. Budget for a fork or a migration to Laravel Scout / whereFullText / a LIKE scope. Do not build a new feature on this without an exit plan.
  • SQL injection surface. Searchy constructs SQL strings around the search term. Always dump ->getQuery()->toSql() with a hostile input like ' OR 1=1 -- and verify quoting. Never let user input reach the table name or column list — those come from the magic method call, so hardcode them.
  • Leading wildcards mean full table scans. This is the dominant performance cost. Measure with EXPLAIN before shipping to a table over a few thousand rows; the relevance subquery runs per row.
  • The levenshtein driver will fail silently-ish if the DB function isn't installed — you get a SQL error on first query, or an empty result set on some setups. It is MySQL-specific; on SQLite/Postgres use fuzzy or simple and keep your test suite on a driver that works there.
  • relevance is an appended column, not a real one. If your table already has a relevance column you'll get a collision; if you select() explicitly you may shadow it. It disappears the moment you re-query the models through Eloquent.
  • No pagination, no chunking. get() loads everything. Always limit().
  • Config caching. After editing config/searchy.php, run php artisan config:cache (and config:clear in dev) or your driver changes won't take effect.
  • Facade alias registration happens via the service provider; on Laravel 5.5+ auto-discovery covers it, but on a manual/older setup you need the Searchy alias in config/app.php.
  • Debug by SQL, not by results. getQuery()->toSql() and getQuery()->getBindings() tell you far more than staring at relevance scores; the fuzzy weighting logic is not obvious from the source and varies by term length and whitespace.
  • Case sensitivity is your DB collation's problem, not the package's — utf8mb4_general_ci gives case-insensitive matching, _bin does not.
  • Test with known-good terms. Because the drivers tokenise on spaces, hyphenated names, apostrophes, and multi-word queries behave inconsistently; write a handful of feature tests pinning the exact expected relevance ordering before you refactor anything.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console