Searching Model data using laravel searchable package

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.

tgugnani: Web Stuff Enthusiast.