Search by

maize-tech / laravel-searchable

maize-tech

Laravel Searchable

Package info

github.com/maize-tech/laravel-searchable

pkg:composer/maize-tech/laravel-searchable

Statistics

Installs: 60 444

Dependents: 0

Suggesters: 0

Stars: 113

Open Issues: 0

4.3.0 2026-11-11 17:34 UTC

This package is auto-updated.

Last update: 2026-11-11 17:35:20 UTC


README

Social Card of Laravel Searchable

Laravel Searchable 🔍

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Easily add weighted searches through model attributes and relationships.

This package currently supports MySQL, PostgreSQL and SQLite (see SQLite support for its requirements and differences).

Installation

You can install the package via composer:

composer require maize-tech/laravel-searchable

You can publish the config file with:

php artisan vendor:publish --provider="Maize\Searchable\SearchableServiceProvider" --tag="searchable-config"

This is the content of the published config file:

return [
    /*
    |--------------------------------------------------------------------------
    | Default match weight
    |--------------------------------------------------------------------------
    |
    | The weight of all searched words which match at least one of the
    | list of searchable attributes.
    | Defaults to 1.
    |
    */

    'default_match_weight' => 1,
];

Usage

To use the package, add the Maize\Searchable\HasSearch trait to each model you want to make searchable.

Once done, you can implement the getSearchableAttributes abstract method by returning the list of attributes (or relationships' attributes) you want to search for.

You can also define the weight of each searchable attribute. If no weight is specified then default_match_weight will be taken from config/searchable.php.

Here's an example model including the HasSearch trait:

<?php

namespace App\Models;

use Maize\Searchable\HasSearch;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Facades\DB;

class Article extends Model
{
    use HasSearch;

    protected $fillable = [
        'id',
        'title',
        'body',
        'creator_name',
        'creator_surname',
    ];

    protected $casts = [
        'body' => 'array',
    ];

    /**
     * Get the model's searchable attributes.
     *
     * @return array
     */
    public function getSearchableAttributes(): array
    {
        return [
            'title' => 5, // Model attribute
            'body.en' => 2, // Single json key of a model attribute
            'tags.name', // Relationship attribute
            'tags.description.*', // All json keys of a relationship attribute
            DB::raw("CONCAT(creator_name, ' ', creator_surname)"), // Raw expressions are supported too
        ];
    }

    /**
     * Allows fetching the tags bound to current article instance
     *
     * @return BelongsToMany
     */
     public function tags(): BelongsToMany
     {
        return $this->belongsToMany(Tag::class)->withTimestamps();
     }
}

Now you can just search for a given term using the scopeSearch scope method:

use App\Models\Article;

$searchTerm = 'the search string';

Article::query()
    ->search($searchTerm)
    ->where('column', '=', 'something')
    ->get();

That's all!

The package generates an SQL query with an 'or' condition for each search term and each searchable fields. The given query returns all models matching the search terms. Furthermore, search results are weighted, which means the query will be ordered by the most matching models.

If you don't want to order the search results by its match weight, you can set the orderByWeight flag to false:

use App\Models\Article;

$searchTerm = 'the search string';

Article::query()
    ->search($searchTerm, false)
    ->where('column', '=', 'something')
    ->get();

SQLite support

Plain columns, relationship attributes, raw expressions, weights and json attributes are all supported on SQLite, with the following requirements and differences:

  • JSON functions: json attributes rely on the json_extract and json_each functions, which are built into SQLite since version 3.38 (older versions must be compiled with the JSON1 extension). The searched column must contain valid json or NULL, otherwise SQLite raises a malformed JSON error.
  • Json wildcard (attribute.*): the search is applied to the top level values of the json object only. Json keys are never matched. Nested objects and arrays are searched in their raw json form, where non-ASCII characters may be stored as escaped sequences (e.g. \u00e0) and are therefore not matched.
  • Case sensitivity: SQLite's LOWER function and LIKE operator only fold ASCII characters. Searches are case insensitive for ASCII characters (APPLE matches apple), and lowercase non-ASCII characters are matched as is (both cittĂ  and CITTĂ€ match a stored CittĂ ). A stored uppercase non-ASCII character is never matched by its own letter though (neither Ă bc nor Ă€BC match a stored Ă€BC).
  • Accent sensitivity: searches are accent sensitive (citta does not match CittĂ ).
  • Whole json columns: when a json column is searched as a plain attribute (body instead of body.en or body.*) the raw json text is searched, so escaped non-ASCII characters are not matched. Use a json key or the wildcard selector instead.
  • Raw expressions: expressions are sent to the database as they are, so they must be valid SQLite. As an example, the CONCAT function is only available since SQLite 3.44: use the || operator on older versions.
  • Amount of results: the keys of all matching models are bound to the final query, which is limited by SQLite's maximum number of bound parameters (32766 by default since SQLite 3.32).

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.