PHPackages                             jesprna/jaysfi-search-string - 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. jesprna/jaysfi-search-string

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

jesprna/jaysfi-search-string
============================

Generates database queries based on one unique string using a simple and customizable syntax.

v1.0.0(4mo ago)0428↓50%MITPHPPHP ^8.1

Since Jan 10Pushed 4mo agoCompare

[ Source](https://github.com/jesprna/laravel-search-string)[ Packagist](https://packagist.org/packages/jesprna/jaysfi-search-string)[ GitHub Sponsors](https://github.com/jesprna)[ RSS](/packages/jesprna-jaysfi-search-string/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (5)Versions (2)Used By (0)

🔍 Laravel Search String
=======================

[](#-laravel-search-string)

[![Latest Version on Packagist](https://camo.githubusercontent.com/09697acc0edc9105f99318b5db99fb9c53e9f9fc5fcb175539e1f59f1bd6b451/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a657370726e612f6a61797366692d7365617263682d737472696e672e737667)](https://packagist.org/packages/jesprna/jaysfi-search-string)[![GitHub Tests Action Status](https://camo.githubusercontent.com/843668bc40c101b363651e9613b7616749d825a179569a9679052d0a7871ea58/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f6a657370726e612f6a61797366692d7365617263682d737472696e672f54657374733f6c6162656c3d7465737473)](https://github.com/jesprna/jaysfi-search-string/actions?query=workflow%3ATests+branch%3Anext)[![Total Downloads](https://camo.githubusercontent.com/d14a1ea255c1e9710291bef0b506e5b4ee6e98215955a17a6cd1d984c7a2825d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6a657370726e612f6a61797366692d7365617263682d737472696e672e737667)](https://packagist.org/packages/jesprna/jaysfi-search-string)

Generates database queries based on one unique string using a simple and customizable syntax.

[![Example of a search string syntax and its result](https://user-images.githubusercontent.com/3642397/40266921-6f7b4c70-5b54-11e8-8e40-000ae3b4e201.png)](https://user-images.githubusercontent.com/3642397/40266921-6f7b4c70-5b54-11e8-8e40-000ae3b4e201.png)

Introduction
------------

[](#introduction)

Laravel Search String provides a simple solution for scoping your database queries using a human readable and customizable syntax. It will transform a simple string into a powerful query builder.

For example, the following search string will fetch the latest blog articles that are either not published or titled "My blog article".

```
Article::usingSearchString('title:"My blog article" or not published sort:-created_at');

// Equivalent to:
Article::where('title', 'My blog article')
       ->orWhere('published', false)
       ->orderBy('created_at', 'desc');
```

This next example will search for the term "John" on the `customer` and `description` columns whilst making sure the invoices are either paid or archived.

```
Invoice::usingSearchString('John and status in (Paid,Archived) limit:10 from:10');

// Equivalent to:
Invoice::where(function ($query) {
           $query->where('customer', 'like', '%John%')
               ->orWhere('description', 'like', '%John%');
       })
       ->whereIn('status', ['Paid', 'Archived'])
       ->limit(10)
       ->offset(10);
```

You can also query for the existence of related records, for example, articles published in 2020, which have more than 100 comments that are either not spam or written by John.

```
Article::usingSearchString('published = 2020 and comments: (not spam or author.name = John) > 100');

// Equivalent to:
Article::where('published_at', '>=', '2020-01-01 00:00:00')
        ->where('published_at', '', 100);
```

As you can see, not only it provides a convenient way to communicate with your Laravel API (instead of allowing dozens of query fields), it also can be presented to your users as a tool to explore their data.

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

[](#installation)

```
# Install via composer
composer require jesprna/jaysfi-search-string

# (Optional) Publish the search-string.php configuration file
php artisan vendor:publish --tag=search-string
```

Basic usage
-----------

[](#basic-usage)

Add the `SearchString` trait to your models and configure the columns that should be used within your search string.

```
use Jesprna\LaravelSearchString\Concerns\SearchString;

class Article extends Model
{
    use SearchString;

    protected $searchStringColumns = [
        'title', 'body', 'status', 'rating', 'published', 'created_at',
    ];
}
```

Note that you can define these in [other parts of your code](#other-places-to-configure) and [customise the behaviour of each column](#configuring-columns).

That's it! Now you can create a database query using the search string syntax.

```
Article::usingSearchString('title:"Hello world" sort:-created_at,published')->get();
```

The search string syntax
------------------------

[](#the-search-string-syntax)

Note that the spaces between operators don't matter.

### Exact matches

[](#exact-matches)

```
'rating: 0'
'rating = 0'
'title: Hello'               // Strings without spaces do not need quotes
'title: "Hello World"'       // Strings with spaces require quotes
"title: 'Hello World'"       // Single quotes can be used too
'rating = 99.99'
'created_at: "2018-07-06 00:00:00"'
```

### Comparisons

[](#comparisons)

```
'title < B'
'rating > 3'
'created_at >= "2018-07-06 00:00:00"'
```

### Lists

[](#lists)

```
'title in (Hello, Hi, "My super article")'
'status in(Finished,Archived)'
'status:Finished,Archived'
```

### Dates

[](#dates)

The column must either be cast as a date or explicitly marked as a date in the [column options](#date).

```
// Year precision
'created_at >= 2020'                    // 2020-01-01 00:00:00  2020-12-31 23:59:59

// Month precision
'created_at = 01/2020'                  // 2020-01-01 00:00:00  10 and not spam)'   // Has non-spam comments with more than 10 likes

// "WhereHas" shortcuts
'comments.title: Superbe'               // Same as 'comments: (title: Superbe)'
'not comments.title: Superbe'           // Same as 'not comments: (title: Superbe)'
'comments.spam'                         // Same as 'comments: (spam)'
'not comments.spam'                     // Same as 'not comments: (spam)'
'comments.likes < 5'                    // Same as 'comments: (likes < 5)'
'not comments.likes < 5'                // Same as 'not comments: (likes < 5)'

// Nested relationships
'comments: (author: (name: John))'      // Has comments from the author named John
'comments.author: (name: John)'         // Same as before
'comments.author.name: John'            // Same as before

// Nested relationships are optimised
'comments.author.name: John and comments.author.age > 21'   // Same as: 'comments: (author: (name: John and age > 21))
'comments.likes > 10 or comments.author.age > 21'           // Same as: 'comments: (likes > 10 or author: (age > 21))
```

Note that all these expressions delegate to the `has` query method. Therefore, it works out-of-the-box with the following relationship types: `HasOne`, `HasMany`, `HasOneThrough`, `HasManyThrough`, `BelongsTo`, `BelongsToMany`, `MorphOne`, `MorphMany` and `MorphToMany`.

The only relationship type currently not supported is `MorphTo` since Laravel Search String needs an explicit related model to use withing nested queries.

### Special keywords

[](#special-keywords)

Note that these keywords [can be customised](#configuring-special-keywords).

```
'fields:title,body,created_at'  // Select only title, body, created_at
'not fields:rating'             // Select all columns but rating
'sort:rating,-created_at'       // Order by rating asc, created_at desc
'limit:1'                       // Limit 1
'from:10'                       // Offset 10
```

Configuring columns
-------------------

[](#configuring-columns)

### Column aliases

[](#column-aliases)

If you want a column to be queried using a different name, you can define it as a key/value pair where the key is the database column name and the value is the alias you wish to use.

```
protected $searchStringColumns = [
    'title',
    'body' => 'content',
    'published_at' => 'published',
    'created_at' => 'created',
];
```

You can also provide a regex pattern for a more flexible alias definition.

```
protected $searchStringColumns = [
    'published_at' => '/^(published|live)$/',
    // ...
];
```

### Column options

[](#column-options)

You can configure a column even further by assigning it an array of options.

```
protected $searchStringColumns = [
    'created_at' => [
        'key' => 'created',         // Default to column name: /^created_at$/
        'date' => true,             // Default to true only if the column is cast as date.
        'boolean' => true,          // Default to true only if the column is cast as boolean or date.
        'searchable' => false       // Default to false.
        'relationship' => false     // Default to false.
        'map' => ['x' => 'y']       // Maps data from the user input to the database values. Default to [].
    ],
    // ...
];
```

#### Key

[](#key)

The `key` option is what we've been configuring so far, i.e. the alias of the column. It can be either a regex pattern (therefore allowing multiple matches) or a regular string for an exact match.

#### Date

[](#date)

If a column is marked as a `date`, the value of the query will be parsed using `Carbon` whilst keeping the level of precision given by the user. For example, if the `created_at` column is marked as a `date`:

```
'created_at >= tomorrow' // Equivalent to:
$query->where('created_at', '>=', 'YYYY-MM-DD 00:00:00');
// where `YYYY-MM-DD` matches the date of tomorrow.

'created_at = "July 6, 2018"' // Equivalent to:
$query->where('created_at', '>=', '2018-07-06 00:00:00');
      ->where('created_at', ' $this->searchStringColumns ?? [],
        'keywords' => $this->searchStringKeywords ?? [],
    ];
}
```

If you'd rather not define any of these configurations on the model itself, you can define them directly on the `config/search-string.php` file like this:

```
// config/search-string.php
return [
    'default' => [
        'keywords' => [ /* ... */ ],
    ],

    Article::class => [
        'columns'  => [ /* ... */ ],
        'keywords' => [ /* ... */ ],
    ],
];
```

When resolving the options for a particular model, LaravelSearchString will merge those configurations in the following order:

1. First using the configurations defined on the model
2. Then using the config file at the key matching the model class
3. Then using the config file at the `default` key
4. Finally using some fallback configurations

Configuring case insensitive searches
-------------------------------------

[](#configuring-case-insensitive-searches)

When using databases like PostgreSql, you can override the default behavior of case sensitive searches by setting case\_insensitive to true in your options amongst columns and keywords. For example, in the config/search-string.php

```
return [
    'default' => [
        'case_insensitive' => true, //  [
        'case_insensitive' => true, // whereRaw("LOWER($column) LIKE ?", ["%$value%"]);

```

Error handling
--------------

[](#error-handling)

The provided search string can be invalid for numerous reasons.

- It does not comply to the search string syntax
- It tries to query an inexisting column or column alias
- It provides invalid values to special keywords like `limit`
- Etc.

Any of those errors will throw an `InvalidSearchStringException`.

However you can choose whether you want these exceptions to bubble up to the Laravel exception handler or whether you want them to fail silently. For that, you need to choose a fail strategy on your `config/search-string.php` configuration file:

```
// config/search-string.php
return [
    'fail' => 'all-results', // (Default) Silently fail with a query containing everything.
    'fail' => 'no-results',  // Silently fail with a query containing nothing.
    'fail' => 'exceptions',  // Throw exceptions.

    // ...
];
```

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance78

Regular maintenance activity

Popularity17

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 90.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

Unknown

Total

1

Last Release

120d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/999042c0244396cbd9fbee3654cbc6ab436604538a3b3daac92505b4bfbec06f?d=identicon)[jesprna](/maintainers/jesprna)

---

Top Contributors

[![lorisleiva](https://avatars.githubusercontent.com/u/3642397?v=4)](https://github.com/lorisleiva "lorisleiva (172 commits)")[![denisdulici](https://avatars.githubusercontent.com/u/5254835?v=4)](https://github.com/denisdulici "denisdulici (7 commits)")[![mbardelmeijer](https://avatars.githubusercontent.com/u/1583095?v=4)](https://github.com/mbardelmeijer "mbardelmeijer (4 commits)")[![jesprna](https://avatars.githubusercontent.com/u/48647209?v=4)](https://github.com/jesprna "jesprna (2 commits)")[![mazedlx](https://avatars.githubusercontent.com/u/9453522?v=4)](https://github.com/mazedlx "mazedlx (2 commits)")[![kharysharpe](https://avatars.githubusercontent.com/u/278654?v=4)](https://github.com/kharysharpe "kharysharpe (1 commits)")[![kylwes](https://avatars.githubusercontent.com/u/39100382?v=4)](https://github.com/kylwes "kylwes (1 commits)")[![mikerockett](https://avatars.githubusercontent.com/u/4586280?v=4)](https://github.com/mikerockett "mikerockett (1 commits)")[![morloderex](https://avatars.githubusercontent.com/u/5677808?v=4)](https://github.com/morloderex "morloderex (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/jesprna-jaysfi-search-string/health.svg)

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

###  Alternatives

[lorisleiva/laravel-search-string

Generates database queries based on one unique string using a simple and customizable syntax.

780409.6k2](/packages/lorisleiva-laravel-search-string)[mailerlite/laravel-elasticsearch

An easy way to use the official PHP ElasticSearch client in your Laravel applications.

934529.3k2](/packages/mailerlite-laravel-elasticsearch)[jeroen-g/explorer

Next-gen Elasticsearch driver for Laravel Scout.

397612.3k](/packages/jeroen-g-explorer)[swisnl/laravel-fulltext

Fulltext indexing and searching for Laravel

184104.5k5](/packages/swisnl-laravel-fulltext)[romanstruk/manticore-scout-engine

Laravel Manticore Scout Engine

4818.1k](/packages/romanstruk-manticore-scout-engine)[statamic-rad-pack/meilisearch

meilisearch search driver for Statamic

1661.7k](/packages/statamic-rad-pack-meilisearch)

PHPackages © 2026

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