PHPackages                             jamal/universal-search-filter - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. jamal/universal-search-filter

ActiveLibrary

jamal/universal-search-filter
=============================

Universal Search Filter: One-box search across columns, relations, and casts for Laravel.

v1.0.0(8mo ago)01MITPHPPHP &gt;=8.1

Since Sep 8Pushed 8mo agoCompare

[ Source](https://github.com/jamalnasir/universal-search-filter)[ Packagist](https://packagist.org/packages/jamal/universal-search-filter)[ RSS](/packages/jamal-universal-search-filter/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (5)Versions (2)Used By (0)

Universal Search Filter for Laravel
===================================

[](#universal-search-filter-for-laravel)

A drop-in trait and query builder macro that turns a single search box into rich conditions across selected columns, relations, and casts — with optional Scout integration and relevance ranking.

Why?
----

[](#why)

Most apps need a "one box" search that just works across names, emails, and related records without hand-writing SQL each time. This package gives you a clean `universalSearch()` scope plus a `whereUniversalSearch()` macro you can apply to any Eloquent query.

Features
--------

[](#features)

- Single text box -&gt; many columns and relations
- Column weights and simple relevance ordering
- Fuzzy matching (configurable)
- Works with relations via `whereHas`
- Pagination friendly (`paginate()` as usual)
- Optional Laravel Scout adapter (when installed)
- Zero-config defaults + per-model config overrides

Installation
------------

[](#installation)

```
composer require jamal/universal-search-filter
php artisan vendor:publish --tag=config --provider="Jamal\\UniversalSearchFilter\\UniversalSearchServiceProvider"
```

This publishes `config/universal_search.php` to tweak defaults.

Quick Start
-----------

[](#quick-start)

In your Eloquent model:

```
use Illuminate\Database\Eloquent\Model;
use Jamal\UniversalSearchFilter\Traits\UniversalSearchable;

class User extends Model
{
    use UniversalSearchable;

    // Optional per-model config
    protected array $universalSearch = [
        'columns' => ['name', 'email', 'phone'],
        'relations' => [
            'posts' => ['title', 'body'],
        ],
        'weights' => [
            'name' => 3,
            'email' => 2,
            'posts' => 1, // weight applied to each related column
        ],
        'fuzzy' => true,
        'exclude_zero_relevancy' => true
    ];
}
```

Then in a controller:

```
$search = request('q');

$users = User::query()
    ->universalSearch($search) // or ->whereUniversalSearch($search)
    ->paginate(15);
```

The scope adds a computed `_universal_relevance` column, and orders by it (higher is better).

### What is weight?

[](#what-is-weight)

Weight is simply a number that shows how important a column (or relation) is compared to others when calculating the relevance score.

Here is how it works:

- When you search, the package checks each column you configured (like `name`, `email`, `phone`) to see if the search tokens match.
- Each match adds to a computed column called \_universal\_relevance.
- The weight you assign decides how many “points” a match in that column adds.

For example:

```
protected array $universalSearch = [
        'columns' => [
            'name',
            'email',
            'status'
        ],
        'weights' => [
            'name' => 5,   // highest importance
            'email' => 3,
            'status' => 1, // least important
        ],
];
```

If you search for `"alice"`:

- A hit in the `name` column adds 5 points.
- A hit in the `email` column adds 3 points.
- A hit in the `status` column adds only 1 point.

The query then orders by `_universal_relevance DESC`, so results with matches in high-weighted columns appear first.

So weight = ranking priority. It does not remove columns from search, it just decides which matches push a record higher in the list.

Using the Query Builder Macro
-----------------------------

[](#using-the-query-builder-macro)

If you are on the Query Builder directly (no model) and want a simple multi-column search:

```
DB::table('users')
    ->whereUniversalSearchOn($search, ['name', 'email', 'phone'])
    ->paginate(20);
```

Scout (Optional)
----------------

[](#scout-optional)

If you have Laravel Scout and your model uses the `Searchable` trait, enable the setting in `config/universal_search.php`:

```
'use_scout_when_available' => true,
```

The scope will route searches via Scout, then hydrate your Eloquent query based on the keys returned. If no results are found, it falls back to the Eloquent driver.

Configuration
-------------

[](#configuration)

`config/universal_search.php`:

- `columns`: default columns when a model does not provide its own list
- `relations`: relation =&gt; \[columns\] pairs
- `weights`: increase importance of certain columns/relations
- `fuzzy`: `true` uses `%token%`, `false` uses prefix `token%`
- `min_token_length`: ignore very short tokens
- `use_scout_when_available`: prefer Scout when the model is searchable

Notes on Relevance
------------------

[](#notes-on-relevance)

The package builds a simple relevance score with a `CASE WHEN column LIKE ? THEN weight` sum across tokens and columns. This is portable and works on MySQL, Postgres, and SQLite. For advanced ranking, consider switching to Scout with a dedicated engine (e.g., Meilisearch, Algolia, TNTSearch).

Examples
--------

[](#examples)

Search users by "john doe" across users and their posts:

```
$users = User::universalSearch('"john doe"')
    ->with('posts')
    ->paginate();
```

Provide options at call site:

```
$users = User::universalSearch($search, [
    'fuzzy' => false, // switch to prefix matching
    'min_token_length' => 3,
]);
```

Use only the macro with manual columns:

```
$orders = Order::query()
    ->whereUniversalSearch($search, ['columns' => ['reference', 'status']])
    ->paginate();
```

Testing (stub)
--------------

[](#testing-stub)

You can write tests by making a few models with the trait and seeding a handful of rows, then asserting counts and order of `_universal_relevance`.

License
-------

[](#license)

MIT

Tailored Defaults
-----------------

[](#tailored-defaults)

Out of the box, the config ships with useful defaults:

- `columns`: name, email, title, phone, reference, status
- `weights`: name (5), email (4), title/reference (3), status (1)
- Empty `relations` so you can opt-in per model

Override at the model level by defining `$universalSearch` or globally by publishing the config.

Running Tests
-------------

[](#running-tests)

This package uses PHPUnit with Orchestra Testbench.

```
composer install
composer test
```

The test suite provisions an in-memory SQLite database, runs simple migrations, seeds demo data, then verifies:

- token parsing for quoted phrases
- column and relation search results
- relevance column is appended and used for ordering
- macro usage on the plain query builder
- fuzzy vs prefix matching

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance61

Regular maintenance activity

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

248d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/db863171f0f4e8b05e333bf1d9439e5869f084fe2a5c0c3df146bbe56d2fe57c?d=identicon)[jamalnasir](/maintainers/jamalnasir)

---

Top Contributors

[![jamalnasir](https://avatars.githubusercontent.com/u/1989782?v=4)](https://github.com/jamalnasir "jamalnasir (6 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/jamal-universal-search-filter/health.svg)

```
[![Health](https://phpackages.com/badges/jamal-universal-search-filter/health.svg)](https://phpackages.com/packages/jamal-universal-search-filter)
```

###  Alternatives

[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k25.9M107](/packages/laravel-cashier)[laravel/scout

Laravel Scout provides a driver based solution to searching your Eloquent models.

1.7k49.4M479](/packages/laravel-scout)[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[yajra/laravel-oci8

Oracle DB driver for Laravel via OCI8

8703.0M17](/packages/yajra-laravel-oci8)[mehdi-fathi/eloquent-filter

Eloquent Filter adds custom filters automatically to your Eloquent Models in Laravel.It's easy to use and fully dynamic, just with sending the Query Strings to it.

450191.6k1](/packages/mehdi-fathi-eloquent-filter)[fumeapp/modeltyper

Generate TypeScript interfaces from Laravel Models

196277.9k](/packages/fumeapp-modeltyper)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
