PHPackages                             konekt/search - 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. [Database &amp; ORM](/categories/database)
4. /
5. konekt/search

ActiveLibrary[Database &amp; ORM](/categories/database)

konekt/search
=============

Laravel package to search through multiple Eloquent models. Supports pagination, eager loading relations, single/multiple columns, sorting and scoped queries.

1.5.0(1y ago)240.7k↓12.5%11MITPHPPHP ^8.1CI failing

Since Jul 7Pushed 1y ago1 watchersCompare

[ Source](https://github.com/artkonekt/search)[ Packagist](https://packagist.org/packages/konekt/search)[ Docs](https://github.com/artkonekt/search)[ RSS](/packages/konekt-search/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (5)Versions (11)Used By (1)

Multi-model Eloquent Search
===========================

[](#multi-model-eloquent-search)

[![Tests](https://camo.githubusercontent.com/912e30566be9cec9753929e860db9a1a96c24971ca68b26d001a25db6494277a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6172746b6f6e656b742f7365617263682f74657374732e796d6c3f6272616e63683d6d6173746572267374796c653d666c61742d737175617265)](https://github.com/artkonekt/search/actions?query=workflow%3Atests)[![Packagist version](https://camo.githubusercontent.com/decc6ce1f60ebc36a9a55c081edf1e86fb0877895ee1a05fb5f56ebd390c3daa/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6b6f6e656b742f7365617263682e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/konekt/search)[![Packagist downloads](https://camo.githubusercontent.com/bce4a5a993a5fbfda3131948f6546b79245ae05be1bb59acb10db7cfe1250c79/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6b6f6e656b742f7365617263682e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/konekt/search)[![StyleCI](https://camo.githubusercontent.com/44a2d27c42966c18ae13dc5e07ad24743821ad626d6629ad4e9153869400dc07/68747470733a2f2f7374796c6563692e696f2f7265706f732f3934323038373232362f736869656c643f6272616e63683d6d6173746572)](https://styleci.io/repos/942087226)[![MIT Software License](https://camo.githubusercontent.com/942e017bf0672002dd32a857c95d66f28c5900ab541838c6c664442516309c8a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)

> This package is a fork of the wonderful [Laravel Cross Eloquent Search](https://github.com/protonemedia/laravel-cross-eloquent-search) package.
> Consider [sponsoring the original author](https://github.com/sponsors/pascalbaljet) of this package.

This Laravel package allows you to search through multiple Eloquent models. It supports sorting, pagination, scoped queries, eager load relationships, and searching through single or multiple columns.

Requirements
------------

[](#requirements)

- PHP 8.0+
- MySQL 8.0 (all features)
- PostgreSQL 11+ (partial feature set)
- SQLite (partial feature set)
- Laravel 9.x, 10.x, 11.x, 12.x

### Feature Matrix

[](#feature-matrix)

FeatureMySQL 8PostgreSQLSQLiteMulti-model search✔✔✔Multi-model pagination✔✔✔Search in JSON fields✔✔✔Sounds like search✔✔❌Full Text Search✔❌❌Sort by Model Order✔❌❌Features
--------

[](#features)

- Search through one or more [Eloquent models](https://laravel.com/docs/master/eloquent).
- Support for cross-model [pagination](https://laravel.com/docs/master/pagination#introduction).
- Search through single or multiple columns.
- Search through (nested) relationships.
- Support for Full-Text Search, even through relationships.
- Order by (cross-model) columns or by relevance.
- Use [constraints](https://laravel.com/docs/master/eloquent#retrieving-models) and [scoped queries](https://laravel.com/docs/master/eloquent#query-scopes).
- [Eager load relationships](https://laravel.com/docs/master/eloquent-relationships#eager-loading) for each model.
- In-database [sorting](https://laravel.com/docs/master/queries#ordering-grouping-limit-and-offset) of the combined result.
- Zero third-party dependencies

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

[](#installation)

You can install the package via composer:

```
composer require konekt/search
```

- If you want to use the `soundsLike()` similarity search with PostgreSQL, then you need to run `CREATE EXTENSION pg_trgm;` once on the given database to enable the [Trigram Extension](https://www.postgresql.org/docs/current/pgtrgm.html).

Usage
-----

[](#usage)

Start your search query by adding one or more models to search through. Call the `add` method with the model's class name and the column you want to search through. Then call the `search` method with the search term, and you'll get a `\Illuminate\Database\Eloquent\Collection` instance with the results.

The results are sorted in ascending order by the *updated* column by default. In most cases, this column is `updated_at`. If you've [customized](https://laravel.com/docs/master/eloquent#timestamps) your model's `UPDATED_AT` constant, or overwritten the `getUpdatedAtColumn` method, this package will use the customized column. If you don't use timestamps at all, it will use the primary key by default. Of course, you can [order by another column](#sorting) as well.

```
use Konekt\Search\Facades\Search;

$results = Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->search('howto');
```

If you care about indentation, you can optionally use the `new` method on the facade:

```
Search::new()
    ->add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->search('howto');
```

There's also an `when` method to apply certain clauses based on another condition:

```
Search::new()
    ->when($user->isVerified(), fn($search) => $search->add(Post::class, 'title'))
    ->when($user->isAdmin(), fn($search) => $search->add(Video::class, 'title'))
    ->search('howto');
```

### Wildcards

[](#wildcards)

By default, we split up the search term, and each keyword will get a wildcard symbol to do partial matching. Practically this means the search term `apple ios` will result in `apple%` and `ios%`. If you want a wildcard symbol to begin with as well, you can call the `beginWithWildcard` method. This will result in `%apple%` and `%ios%`.

```
Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->beginWithWildcard()
    ->search('os');
```

If you want to disable the behaviour where a wildcard is appended to the terms, you should call the `endWithWildcard` method with `false`:

```
Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->beginWithWildcard()
    ->endWithWildcard(false)
    ->search('os');
```

### Multi-word search

[](#multi-word-search)

Multi-word search is supported out of the box. Simply wrap your phrase into double-quotes.

```
Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->search('"macos big sur"');
```

You can disable the parsing of the search term by calling the `dontParseTerm` method, which gives you the same results as using double-quotes.

```
Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->dontParseTerm()
    ->search('macos big sur');
```

### Sorting

[](#sorting)

If you want to sort the results by another column, you can pass that column to the `add` method as a third parameter. Call the `orderByDesc` method to sort the results in descending order.

```
Search::add(Post::class, 'title', 'published_at')
    ->add(Video::class, 'title', 'released_at')
    ->orderByDesc()
    ->search('learn');
```

You can call the `orderByRelevance` method to sort the results by the number of occurrences of the search terms. Imagine these two sentences:

- Apple introduces iPhone 13 and iPhone 13 mini
- Apple unveils new iPad mini with breakthrough performance in stunning new design

If you search for *Apple iPad*, the second sentence will come up first, as there are more matches of the search terms.

```
Search::add(Post::class, 'title')
    ->beginWithWildcard()
    ->orderByRelevance()
    ->search('Apple iPad');
```

Ordering by relevance is *not* supported if you're searching through (nested) relationships.

To sort the results by model type, you can use the `orderByModel` method by giving it your preferred order of the models:

```
Search::new()
    ->add(Comment::class, ['body'])
    ->add(Post::class, ['title'])
    ->add(Video::class, ['title', 'description'])
    ->orderByModel([
        Post::class, Video::class, Comment::class,
    ])
    ->search('Artisan School');
```

### Pagination

[](#pagination)

We highly recommend paginating your results. Call the `paginate` method before the `search` method, and you'll get an instance of `\Illuminate\Contracts\Pagination\LengthAwarePaginator` as a result. The `paginate` method takes three (optional) parameters to customize the paginator. These arguments are [the same](https://laravel.com/docs/master/pagination#introduction) as Laravel's database paginator.

```
Search::add(Post::class, 'title')
    ->add(Video::class, 'title')

    ->paginate()
    // or
    ->paginate($perPage = 15, $pageName = 'page', $page = 1)

    ->search('build');
```

You may also use [simple pagination](https://laravel.com/docs/master/pagination#simple-pagination). This will return an instance of `\Illuminate\Contracts\Pagination\Paginator`, which is not length aware:

```
Search::add(Post::class, 'title')
    ->add(Video::class, 'title')

    ->simplePaginate()
    // or
    ->simplePaginate($perPage = 15, $pageName = 'page', $page = 1)

    ->search('build');
```

### Constraints and scoped queries

[](#constraints-and-scoped-queries)

Instead of the class name, you can also pass an instance of the [Eloquent query builder](https://laravel.com/docs/master/eloquent#retrieving-models) to the `add` method. This allows you to add constraints to each model.

```
Search::add(Post::published(), 'title')
    ->add(Video::where('views', '>', 2500), 'title')
    ->search('compile');
```

### Multiple columns per model

[](#multiple-columns-per-model)

You can search through multiple columns by passing an array of columns as the second argument.

```
Search::add(Post::class, ['title', 'body'])
    ->add(Video::class, ['title', 'subtitle'])
    ->search('eloquent');
```

### Search through (nested) relationships

[](#search-through-nested-relationships)

You can search through (nested) relationships by using the *dot* notation:

```
Search::add(Post::class, ['comments.body'])
    ->add(Video::class, ['posts.user.biography'])
    ->search('solution');
```

### Full-Text Search

[](#full-text-search)

You may use [MySQL's Full-Text Search](https://laravel.com/docs/master/queries#full-text-where-clauses) by using the `addFullText` method. You can search through a single or multiple columns (using [full text indexes](https://laravel.com/docs/master/migrations#available-index-types)), and you can specify a set of options, for example, to specify the mode. You can even mix regular and full-text searches in one query:

```
Search::new()
    ->add(Post::class, 'title')
    ->addFullText(Video::class, 'title', ['mode' => 'boolean'])
    ->addFullText(Blog::class, ['title', 'subtitle', 'body'], ['mode' => 'boolean'])
    ->search('framework -css');
```

If you want to search through relationships, you need to pass in an array where the array key contains the relation, while the value is an array of columns:

```
Search::new()
    ->addFullText(Page::class, [
        'posts' => ['title', 'body'],
        'sections' => ['title', 'subtitle', 'body'],
    ])
    ->search('framework -css');
```

### Sounds like

[](#sounds-like)

MySQL has a *soundex* algorithm built-in so you can search for terms that sound almost the same. You can use this feature by calling the `soundsLike` method:

```
Search::new()
    ->add(Post::class, 'framework')
    ->add(Video::class, 'framework')
    ->soundsLike()
    ->search('larafel');
```

### Eager load relationships

[](#eager-load-relationships)

Not much to explain here, but this is supported as well :)

```
Search::add(Post::with('comments'), 'title')
    ->add(Video::with('likes'), 'title')
    ->search('guitar');
```

### Getting results without searching

[](#getting-results-without-searching)

You call the `search` method without a term or with an empty term. In this case, you can discard the second argument of the `add` method. With the `orderBy` method, you can set the column to sort by (previously the third argument):

```
Search::add(Post::class)
    ->orderBy('published_at')
    ->add(Video::class)
    ->orderBy('released_at')
    ->search();
```

### Counting records

[](#counting-records)

You can count the number of results with the `count` method:

```
Search::add(Post::published(), 'title')
    ->add(Video::where('views', '>', 2500), 'title')
    ->count('compile');
```

### Model Identifier

[](#model-identifier)

You can use the `includeModelType` to add the model type to the search result.

```
Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->includeModelType()
    ->paginate()
    ->search('foo');

// Example result with model identifier.
{
    "current_page": 1,
    "data": [
        {
            "id": 1,
            "video_id": null,
            "title": "foo",
            "published_at": null,
            "created_at": "2021-12-03T09:39:10.000000Z",
            "updated_at": "2021-12-03T09:39:10.000000Z",
            "type": "Post",
        },
        {
            "id": 1,
            "title": "foo",
            "subtitle": null,
            "published_at": null,
            "created_at": "2021-12-03T09:39:10.000000Z",
            "updated_at": "2021-12-03T09:39:10.000000Z",
            "type": "Video",
        },
    ],
    ...
}
```

By default, it uses the `type` key, but you can customize this by passing the key to the method.

You can also customize the `type` value by adding a public method `searchType()` to your model to override the default class base name.

```
class Video extends Model
{
    public function searchType()
    {
        return 'awesome_video';
    }
}

// Example result with searchType() method.
{
    "current_page": 1,
    "data": [
        {
            "id": 1,
            "video_id": null,
            "title": "foo",
            "published_at": null,
            "created_at": "2021-12-03T09:39:10.000000Z",
            "updated_at": "2021-12-03T09:39:10.000000Z",
            "type": "awesome_video",
        }
    ],
    ...
```

### Standalone parser

[](#standalone-parser)

You can use the parser with the `parseTerms` method:

```
$terms = Search::parseTerms('drums guitar');
```

You can also pass in a callback as a second argument to loop through each term:

```
Search::parseTerms('drums guitar', function($term, $key) {
    //
});
```

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance44

Moderate activity, may be stable

Popularity32

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 57.1% 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 ~188 days

Recently: every ~174 days

Total

10

Last Release

441d ago

Major Versions

0.3.0 → 1.0.02023-04-03

PHP version history (4 changes)0.1.0PHP ^7.4

1.0.0PHP ^8.1|^8.2

1.2.0PHP ^8.0

1.5.0PHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/9c398dd02c93ecf6aa344f367f5744aeb32b4c7bbc23b1b22e95336f45bf0d5a?d=identicon)[konekt](/maintainers/konekt)

---

Top Contributors

[![pascalbaljet](https://avatars.githubusercontent.com/u/8403149?v=4)](https://github.com/pascalbaljet "pascalbaljet (80 commits)")[![fulopattila122](https://avatars.githubusercontent.com/u/1162360?v=4)](https://github.com/fulopattila122 "fulopattila122 (50 commits)")[![StyleCIBot](https://avatars.githubusercontent.com/u/11048387?v=4)](https://github.com/StyleCIBot "StyleCIBot (3 commits)")[![laravel-shift](https://avatars.githubusercontent.com/u/15991828?v=4)](https://github.com/laravel-shift "laravel-shift (1 commits)")[![mewejo](https://avatars.githubusercontent.com/u/1097093?v=4)](https://github.com/mewejo "mewejo (1 commits)")[![mrkalmdn](https://avatars.githubusercontent.com/u/10385316?v=4)](https://github.com/mrkalmdn "mrkalmdn (1 commits)")[![bradyrenting](https://avatars.githubusercontent.com/u/14994117?v=4)](https://github.com/bradyrenting "bradyrenting (1 commits)")[![tushargugnani](https://avatars.githubusercontent.com/u/5244323?v=4)](https://github.com/tushargugnani "tushargugnani (1 commits)")[![Daanra](https://avatars.githubusercontent.com/u/6588838?v=4)](https://github.com/Daanra "Daanra (1 commits)")[![gazben](https://avatars.githubusercontent.com/u/3780285?v=4)](https://github.com/gazben "gazben (1 commits)")

---

Tags

searchlaraveleloquentprotonemediakonekt

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/konekt-search/health.svg)

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

###  Alternatives

[anourvalar/eloquent-serialize

Laravel Query Builder (Eloquent) serialization

11120.2M21](/packages/anourvalar-eloquent-serialize)[lacodix/laravel-model-filter

A Laravel package to filter, search and sort models with ease while fetching from database.

17649.9k](/packages/lacodix-laravel-model-filter)[mozex/laravel-scout-bulk-actions

A Laravel Scout extension for bulk importing and flushing of all models.

1033.4k](/packages/mozex-laravel-scout-bulk-actions)[stayallive/laravel-eloquent-observable

Register Eloquent model event listeners just-in-time directly from the model.

2928.9k7](/packages/stayallive-laravel-eloquent-observable)[konekt/acl

Concord Module for Permission handling (Laravel 10 - 12)

1070.8k1](/packages/konekt-acl)

PHPackages © 2026

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