masterro / laravel-searchable
Simple fulltext search though Eloquent models
Requires
- php: ^7.2|^8.0
- illuminate/database: ^6.0|^7.0|^8.0
- illuminate/pagination: ^6.0|^7.0|^8.0
- illuminate/support: ^6.0|^7.0|^8.0
Requires (Dev)
- nesbot/carbon: ^2.27
- orchestra/testbench: ^v5.0|^v6.0
README
Laravel simple FULLTEXT search through multiple Eloquent models
This is a small Laravel package allows you to make a global search though multiple Eloquent models and get ordered by relevance collection of results. It uses MATCH AGAINST MySQL queries.
Installation
Step 1: Composer
From the command line, run:
composer require masterro/laravel-searchable
Step 2: Service Provider
If you do not use laravel package auto-discovery you need to register the service provider, open config/app.php
and, within the providers
array, append:
MasterRO\Searchable\SearchableServiceProvider::class
Usage
Register your search models in AppServiceProvider or create your custom one
Searchable::registerModels([ Post::class, Article::class, User::class, ]);
Then you should implement MasterRO\Searchable\SearchableContract by each registered model, or it will be skipped and define searchable
method
public static function searchable(): array { return ['title', 'description']; }
Make sure you added fulltext indicies to your tables
public function up() { Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->longText('description'); $table->dateTime('published_at')->nullable(); $table->timestamps(); }); DB::statement('ALTER TABLE posts ADD FULLTEXT search(title, description)'); }
Now you can make search in your controller or where you want
public function search(Request $request, Searchable $searchable) { $query = trim($request->input('q')); if (mb_strlen($query) < 3) { return back()->withInput()->withErrors([ 'search_error' => __('messages.search_error') ]); } return view('search.index')->with('results', $searchable->search($query)); }
Runtime search model switching
$result = $this->searchable->searchModel(Post::class, 'consequatur');
$result = $this->searchable->searchModel([Article::class, Post::class], 'consequatur');
Filtering
Model filter
Search results can be filtered by adding the filterSearchResults()
in your model (like Eloquent global scope)
class User extends Model implements SearchableContract { public function posts() { return $this->hasMany(Post::class); } public function filterSearchResults($query) { return $query->whereHas('posts', function ($query) { $query->where('is_published', true); }); } }
The example code above will filter the search results and will only return users which have published posts.
Runtime filter
Search results can be filtered by adding custom filter callback
$result = $this->searchable ->withFilter(function (Builder $query) { return $query->getModel() instanceof Post ? $query->where('description', '!=', 'Doloremque iure sequi quos sequi consequatur.') : $query; }) ->search('Dolorem');
Disabling model filter
Model filters can be skipped in runtime like Eloquent global scopes.
$result = $this->searchable->withoutModelFilters()->search('quia est ipsa molestiae hic');
You can specify models to skip filters for
$result = $this->searchable ->withoutModelFilters(Post::class) ->search('quia est ipsa molestiae hic');
or
$result = $this->searchable ->withoutModelFilters([Article::class, Post::class]) ->search('quia est ipsa molestiae hic');
Eager load (N+1 issue)
For preventing N+1 issue you can eager load relationships for search results
$result = $this->searchable ->with([Article::class => 'author']) ->search('consequatur');
$result = $this->searchable ->with([ Article::class => [ 'author' => function ($query) { return $query->where('active', true); }, ], ]) ->searchModel(Article::class, 'consequatur');