Creating an instant search bar with Laravel & Vue

Photo by Fabian Grohs on Unsplash

Recently I have been working on a new Laravel application, halfway through the project I thought that a cool feature to have would be an instant search bar. They add a nice touch to any application and from a user experience point of view they’re pretty useful. You start to type in a query and you instantly get results back. Simple right?

After doing some searching it turns out this is actually pretty simple, thanks to Laravel Scout and Vue-InstantSearch. So after a little configuration and a Laracasts tutorial, the search bar was up and running and it worked like a dream.

However I had an issue. I wasn’t entirely happy that I had to set up an Algolia account which required managing, also to run Scout as efficiently as possible it would mean setting up a queue and managing that, which seemed overly complicated for what I required.

This led me to look for something simpler and to not have to rely on the services of Scout and Vue-InstantSearch. In my search to implement this feature on my own I came across an incredibly useful package by Spatie (Who I owe a postcard) called spatie/laravel-searchable. This package “makes it easy to get structured search from a variety of sources.” This was exactly what I needed for the back-end. A simple way to query my model without using Algolia/Scout and without a bunch of complex where statements which I wouldn’t understand a few months from now.


Firstly I required the package and configured my Model in accordance to the documentation.

composer require spatie/laravel-searchable

To set up the laravel-searchable package it requires implementing the Searchable inteface on the Model you want to add structured searching to.

<?php
namespace App;
use Spatie\Searchable\Searchable;
use Spatie\Searchable\SearchResult;
use Illuminate\Database\Eloquent\Model;
class Member extends Model implements Searchable
{
public function getSearchResult(): SearchResult
{
       $url = route('members.show', $this->id);

return new SearchResult($this, $this->full_name, $url);
}
}

This all seems pretty simple, you basically new up a SearchResult class and pass it the Model, a “title” for each record, in my case it was my Members full name, and you can also pass a URL which can be used to add a link to each result returned.

I then created a controller to return the data with the hopes of accessing it using Vue and Axios, which I decided to use for my front end implementation.

<?php
namespace App\Http\Controllers;
use App\Member;
use Spatie\Searchable\Search;
use Illuminate\Http\Request;
class MemberSearchController extends Controller
{
  public function index(Request $request)
{
$results = (new Search())
->registerModel(Member::class, ['first_name', 'last_name'])
->search($request->input('query'));

return response()->json($results);
}
}

This method would generate a new search class and register the Member model, the second argument of the registerModel method is to specify the fields on the Model you want the user to be able to search by.

We would then search the model using the request data and return the response in JSON.

Finally for the back-end we add the route to our routes/web file…

Route::get('members/search', 'MemberSearchController@index');

So far so good, we now have a way of searching the Model for a specific query based on user input, but how do we make this instant on the front-end?


On the front-end I created a Vue component, to simplify this write up I’ve include the barebones HTML to get this working.

Here’s the template, on the input tag we use v-model to bind the input to the “query” variable. This will allow us to update the search results as the user types in their query.

<template>
<div>
<input type="text" placeholder="Search" v-model="query">
<ul v-if="results.length > 0 && query">
<li v-for="result in results.slice(0,10)" :key="result.id">
<a :href="result.url">
<div v-text="result.title"></div>
</a>
</li>
</ul>
</div>
</template>

For the script, we set up a watcher on the “query” variable, so that when it changes we make a request to the MemberSearchController (using Axios) to search the Members model for the specific query that has been entered into the input field.

data() {
return {
query: null,
results: []
};
},
watch: {
query(after, before) {
this.searchMembers();
}
},
methods: {
searchMembers() {
axios.get('members/search', { params: { query: this.query } })
.then(response => this.results = response.data)
.catch(error => {});
}
}

The result? an instant search bar that works quite well for this simple use case.

Final search bar with added styling

So that’s how I added an instant search bar to one of my Laravel applications. Feel free to leave a comment or feedback, i’m always looking to improve or hear alternative ways people have done something similar.