PHPackages                             ajcastro/searchable - 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. [Search &amp; Filtering](/categories/search)
4. /
5. ajcastro/searchable

ActiveLibrary[Search &amp; Filtering](/categories/search)

ajcastro/searchable
===================

Pattern-matching search for Laravel eloquent models.

v2.0.0(4y ago)2854.3k↓72.8%5[1 issues](https://github.com/ajcastro/searchable/issues)MITPHPPHP &gt;=5.4.0CI failing

Since Feb 23Pushed 4y ago1 watchersCompare

[ Source](https://github.com/ajcastro/searchable)[ Packagist](https://packagist.org/packages/ajcastro/searchable)[ RSS](/packages/ajcastro-searchable/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (9)Dependencies (2)Versions (17)Used By (0)

Searchable
==========

[](#searchable)

Pattern-matching search for Laravel eloquent models.

- Currently supports MySQL only.
- Helpful for complex table queries with multiple joins and derived columns.
- Fluent columns definitions.

Demo Project
------------

[](#demo-project)

See [demo project](https://github.com/ajcastro/searchable-demo).

Overview
--------

[](#overview)

Simple setup for searchable model and can search on derived columns.

```
use AjCastro\Searchable\Searchable;

class Post
{
    use Searchable;

    protected $searchable = [
        // This will search on the defined searchable columns
        'columns' => [
            'posts.title',
            'posts.body',
            'author_full_name' => 'CONCAT(authors.first_name, " ", authors.last_name)'
        ],
        'joins' => [
            'authors' => ['authors.id', 'posts.author_id']
        ]
    ];

    public function author()
    {
        return $this->belongsTo(Author::class);
    }
}

// Usage
Post::search("Some title or body content or even the author's full name")
    ->with('author')
    ->paginate();
```

Imagine we have an api for a table or list that has searching and column sorting and pagination. This is a usual setup for a table or list. The internal explanations will be available on the documentation below. Our api call may look like this:

`http://awesome-app.com/api/posts?per_page=10&page=1&sort_by=title&descending=true&search=SomePostTitle`

Your code can look like this:

```
class PostsController
{
    public function index(Request $request)
    {
        $query = Post::query();

        return $query
            ->with('author')
            // advance usage with custom search string parsing
            ->when($request->parse_using === 'exact', function ($query) {
                $query->parseUsing(function ($searchStr) {
                    return "%{$searchStr}%";
                });
            })
            ->search($request->search)
            ->when(
                $request->has('sort_by') && $query->getModel()->isColumnValid($request->sort_by),
                function ($query) use ($request) {
                    $query->orderBy(
                        DB::raw($query->getModel()->getColumn($request->sort_by)),
                        $request->descending ? 'desc' : 'asc'
                    );
                },
                function ($query) {
                    $query->sortByRelevance();
                },
            )
            ->paginate();
    }

}
```

Documentation
-------------

[](#documentation)

### Installation

[](#installation)

```
composer require ajcastro/searchable

```

### Searchable Model

[](#searchable-model)

```
use AjCastro\Searchable\Searchable;

class Post extends Model
{
    use Searchable;

    /**
     * Searchable model definitions.
     */
     protected $searchable = [
        // Searchable columns of the model.
        // If this is not defined it will default to all table columns.
        'columns' => [
            'posts.title',
            'posts.body',
            'author_full_name' => 'CONCAT(authors.first_name, " ", authors.last_name)'
        ],
        // This is needed if there is a need to join other tables for derived columns.
        'joins' => [
            'authors' => ['authors.id', 'posts.author_id'], // defaults to leftJoin method of eloquent builder
            'another_table' => ['another_table.id', 'authors.another_table_id', 'join'], // can pass leftJoin, rightJoin, join of eloquent builder.
        ]
    ];

    /**
     * Can also be written like this for searchable columns.
     *
     * @var array
     */
    protected $searchableColumns = [
        'title',
        'body',
        'author_full_name' => 'CONCAT(authors.first_name, " ", authors.last_name)'
    ];

    /**
     * Can also be written like this for searchable joins.
     *
     * @var array
     */
    protected $searchableJoins = [
        'authors' => ['authors.id', 'posts.author_id']
    ];
}

// Usage
// Call search anywhere
// This only search on the defined columns.
Post::search('Some post')->paginate();
Post::where('likes', '>', 100)->search('Some post')->paginate();
```

If you want to sort by relevance, call method `sortByRelevance()` after `search()` method. This will addSelect field `sort_index` which will be used to order or sort by relevance.

Example:

```
Post::search('Some post')->sortByRelevance()->paginate();
Post::where('likes', '>', 100)->search('Some post')->sortByRelevance()->paginate();

```

### Set searchable configurations on runtime.

[](#set-searchable-configurations-on-runtime)

```
$post = new Post;
$post->setSearchable([ // addSearchable() method is also available
    'columns' => [
        'posts.title',
        'posts.body',
    ],
    'joins' => [
        'authors' => ['authors.id', 'posts.author_id']
    ]
]);
// or
$post->setSearchableColumns([ // addSearchableColumns() method is also available
    'posts.title',
    'posts.body',
]);
$post->setSearchableJoins([ // addSearchableJoins() method is also available
    'authors' => ['authors.id', 'posts.author_id']
]);
```

### Easy Sortable Columns

[](#easy-sortable-columns)

You can define columns to be only sortable but not be part of search query constraint. Just put it under `sortable_columns` as shown below . This column can be easily access to put in `orderBy` of query builder. All searchable columns are also sortable columns.

```
class Post {
     protected $searchable = [
        'columns' => [
            'title' => 'posts.title',
        ],
        'sortable_columns' => [
            'status_name' => 'statuses.name',
        ],
        'joins' => [
            'statuses' => ['statuses.id', 'posts.status_id']
        ]
    ];
}

// Usage

Post::search('A post title')->orderBy(Post::make()->getSortableColumn('status_name'));
// This will only perform search on `posts`.`title` column and it will append "order by `statuses`.`name`" in the query.
// This is beneficial if your column is mapped to a different column name coming from front-end request.
```

### Custom Search String Parser - Exact Search Example

[](#custom-search-string-parser---exact-search-example)

Override the `deafultSearchQuery` in the model like so:

```
use AjCastro\Searchable\BaseSearch;

class User extends Model
{
    public function defaultSearchQuery()
    {
        return BaseSearch::make($this->buildSearchableColumns())
            ->parseUsing(function ($searchStr) {
                return $searchStr; // produces "where `column` like '{$searchStr}'"
                return "%{$searchStr}%"; // produces "where `column` like '%{$searchStr}%'"
            });
    }
}
```

You may also check the build query by dd-ing it:

```
$query = User::search('John Doe');
dd($query->toSql());
```

which may output to

```
select * from users where `column` like 'John Doe'
// or
select * from users where `column` like '%John Doe%'

```

### Using derived columns for order by and where conditions

[](#using-derived-columns-for-order-by-and-where-conditions)

Usually we have queries that has a derived columns like our example for `Post`'s `author_full_name`. Sometimes we need to sort our query results by this column.

```
$query = Post::query();
$post = $query->getModel();
// (A)
$query->search('Some search')->orderBy($post->getColumn('author_full_name'), 'desc')->paginate();
// (B)
$query->search('Some search')->where($post->getColumn('author_full_name'), 'William%')->paginate();
```

which may output to

```
-- (A)
select * from posts where ... order by CONCAT(authors.first_name, " ", authors.last_name) desc limit 0, 15;
-- (B)
select * from posts where ... and CONCAT(authors.first_name, " ", authors.last_name) like 'William%' limit 0, 15;
```

Helper methods available
------------------------

[](#helper-methods-available)

### TableColumns::get() \[static\]

[](#tablecolumnsget-static)

- Get the table columns.

```
TableColumns::get('posts');
```

### isColumnValid

[](#iscolumnvalid)

- Identifies if the column is a valid column, either a regular table column or derived column.
- Useful for checking valid columns to avoid sql injection especially in `orderBy` query, [see post](https://freek.dev/1317-an-important-security-release-for-laravel-query-builder).

```
$query->getModel()->isColumnValid(request('sort_by'));
```

### enableSearchable

[](#enablesearchable)

- Enable the searchable behavior.

```
$query->getModel()->enableSearchable();
$query->search('foo');
```

### disableSearchable

[](#disablesearchable)

- Disable the searchable behavior.
- Calling `search()` method will not perform a search.

```
$query->getModel()->disableSearchable();
$query->search('foo');
```

### setSearchable

[](#setsearchable)

- Set or override the model's `$searchable` property.
- Useful for building searchable config on runtime.

```
$query->getModel()->setSearchable([
  'columns' => ['title', 'status'],
  'joins' => [...],
]);
$query->search('foo');
```

### addSearchable

[](#addsearchable)

- Add columns or joins in the model's `$searchable` property.
- Useful for building searchable config on runtime.

```
$query->getModel()->addSearchable([
  'columns' => ['title', 'status'],
  'joins' => [...],
]);
$query->search('foo');
```

Warning
-------

[](#warning)

Calling `select()` after `search()` will overwrite `sort_index` field, so it is recommended to call `select()`before `search()`. Or you can use `addSelect()` instead.

Credits
-------

[](#credits)

- Ray Anthony Madrona [@raymadrona](https://github.com/raymadrona), for the tips on using MySQL `LOCATE()` for sort relevance.
- [nicolaslopezj/searchable](https://github.com/nicolaslopezj/searchable), for the `$searchable` property declaration style.

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance19

Infrequent updates — may be unmaintained

Popularity41

Moderate usage in the ecosystem

Community12

Small or concentrated contributor base

Maturity64

Established project with proven stability

 Bus Factor1

Top contributor holds 99.4% 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

Every ~63 days

Recently: every ~181 days

Total

15

Last Release

1780d ago

Major Versions

v1.5.0 → 2.x-dev2021-07-25

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/4918318?v=4)[Arjon Jason Castro](/maintainers/ajcastro)[@ajcastro](https://github.com/ajcastro)

---

Top Contributors

[![ajcastro](https://avatars.githubusercontent.com/u/4918318?v=4)](https://github.com/ajcastro "ajcastro (153 commits)")[![raymadrona](https://avatars.githubusercontent.com/u/4514908?v=4)](https://github.com/raymadrona "raymadrona (1 commits)")

---

Tags

eloquent-searchlaravellaravel-packagequeryquery-buildersearchablelaravellaravel-packagequeryquery buildereloquent-searchpattern-matching-search

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ajcastro-searchable/health.svg)

```
[![Health](https://phpackages.com/badges/ajcastro-searchable/health.svg)](https://phpackages.com/packages/ajcastro-searchable)
```

###  Alternatives

[ambengers/query-filter

Laravel package for filtering resources with request query string

3413.8k](/packages/ambengers-query-filter)[omaressaouaf/query-builder-criteria

Define reusable query criteria for filtering, sorting, search, field selection, and includes in Laravel Eloquent models

285.3k](/packages/omaressaouaf-query-builder-criteria)[hashemi/queryfilter

A simple &amp; dynamic package for your eloquent query in laravel. It will help you to write query logic individual for each parameter.

391.2k](/packages/hashemi-queryfilter)

PHPackages © 2026

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