This is a detailed tutorial on how to use the Searchable package to search multiple model data in Laravel.
laravel-searchable
package provides an easy way to get structured search results from a variety of sources.
Let's implement it in a sample project which has two Models Product and Category.
Goal here is to be able to search multiple columns in the table and also we should be able to get search results from all three models in a single search result.
Step 1 : Generate Models and Schema
For this demonstration of this project. Let's set up a sample project with three Models along with the database migration schema.
Generate the Models along with migration file
php artisan make:model Product -m
php artisan make:model Category -m
Schema definition of products
table
Schema::create('products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->text('description');
$table->string('image')->nullable();
$table->bigInteger('category_id');
$table->decimal('amount', 8, 2);
$table->timestamps();
$table->softDeletes();
});
Schema definition of categories
table
Schema::create('categories', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('slug');
$table->text('description');
$table->timestamps();
});
Step 2 : Install the laravel-searchable package.
First step is to install the laravel-searchable package. Run the following command in your project terminal / command-line
composer require spatie/laravel-searchable
Step 3 : Setup Models to be Searchable.
Once the package is installed, you can now modify your Models to be searchable. For this, you need to do two things.
1. alter the Model to implement the Searchable
(Spatie\Searchable\Searchable) interface.
2. Implement the required method getSearchResult()
of the Searchable interface.
Here is how the Product
model looks like
<?php
namespace App;
use Spatie\Searchable\Searchable;
use Spatie\Searchable\SearchResult;
use Illuminate\Database\Eloquent\Model;
class Product extends Model implements Searchable
{
protected $guarded = [];
public function category(){
return $this->belongsTo('App\Category');
}
public function getSearchResult(): SearchResult
{
$url = route('admin.gifts.show', $this->id);
return new SearchResult(
$this,
$this->name,
$url
);
}
}
As you see we have altered Product model to implement the Searchable class and also implemented the required method getSearchResults.
getSearchResults
method needs to return class SearchResult
, and which in turn requires three parameters.
1. Which Model to search ($this denotes the current Model class)
2. What needs to be returned in the search results ($this->name denotes we will return product name as part of search result)
3. URL to point to from the search results (Here we are linking to the show URL which shows the single product resource)
Simple, Isn't it.
Similarly, let's alter the Category model as well.
<?php
namespace App;
use Spatie\Searchable\Searchable;
use Spatie\Searchable\SearchResult;
use Illuminate\Database\Eloquent\Model;
class Category extends Model implements Searchable
{
protected $guarded = [];
public function products(){
return $this->hasMany('App\Gift');
}
public function getSearchResult(): SearchResult
{
$url = route('admin.categories.show', $this->id);
return new SearchResult(
$this,
$this->name,
$url
);
}
}
Step 4 : Setup Search Controller, Routes, And View File
Next Up, Let's implement the search functionality.
Generate a new controller
php artisan make:controller SearchController
We will implement two methods in the Search controller.
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use Spatie\Searchable\Search;
use App\Http\Controllers\Controller;
class SearchController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view('search');
}
/**
* search records in database and display results
* @param Request $request [description]
* @return view [description]
*/
public function search( Request $request)
{
$searchterm = $request->input('query');
$searchResults = (new Search())
->registerModel(\App\Product::class, 'name')
->registerModel(\App\Category::class, 'name')
->perform($searchterm);
return view('search', compact('searchResults', 'searchterm'));
}
}
Here is the view file code to display the search results in the structured format as per the model data.
@extends('layouts.admin.admin')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-10">
<form method="get" action="{{ route('search.result') }}" class="form-inline mr-auto">
<input type="text" name="query" value="{{ isset($searchterm) ? $searchterm : '' }}" class="form-control col-sm-8" placeholder="Search events or blog posts..." aria-label="Search">
<button class="btn aqua-gradient btn-rounded btn-sm my-0 waves-effect waves-light" type="submit">Search</button>
</form>
<br>
@if(isset($searchResults))
@if ($searchResults-> isEmpty())
<h2>Sorry, no results found for the term <b>"{{ $searchterm }}"</b>.</h2>
@else
<h2>There are {{ $searchResults->count() }} results for the term <b>"{{ $searchterm }}"</b></h2>
<hr />
@foreach($searchResults->groupByType() as $type => $modelSearchResults)
<h2>{{ ucwords($type) }}</h2>
@foreach($modelSearchResults as $searchResult)
<ul>
<a href="{{ $searchResult->url }}">{{ $searchResult->title }}</a>
</ul>
@endforeach
@endforeach
@endif
@endif
</div>
</div>
</div>
@endsection
And finally, the routes in web.php
file that hits the controller methods.
// search routes
Route::get('search', 'Admin\SearchController@index')->name('search.index');
Route::get('search-results', 'Admin\SearchController@search')->name('search.result');
Here is how the search results looks like.