This tutorial is out of date and no longer maintained.
In a big web application, like an online market or a social network, one of the most important parts of the app is the search functionality. Having a great search feature will help your users find the right content (users, products, articles) quickly and easily.
In this tutorial, we are going to learn, how to enhance your default search field in your site with real-time suggestions from the database. We will be using Laravel, jQuery and Typeahead (Bloodhound as the suggestion engine) to implement the smart search and Bootstrap for the styling. So, let’s get started.
Typeahead.js is a flexible JavaScript library that provides a strong foundation for building robust search auto-completion. Its library consists of two components:
The suggestion engine computes the suggestions for a given query and the UI view renders them. Both can be used separately. In this tutorial, we are going to use both of them. Here is an example of how Typeahead works:
http://codepen.io/balajinatarajan/pen/ocKxC
To get started, you need to pull in Typeahead.js. You can use any of the following methods to install Typeahead.
$ bower install typeahead.js
bloodhound.js
(standalone suggestion engine)typeahead.jquery.js
(standalone UI view)typeahead.bundle.js
(bloodhound.js
+ typeahead.jquery.js
)typeahead.bundle.min.js
https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js
https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js
Note: Both bloodhound.js
and typeahead.jquery.js
have a dependency on jQuery 1.9+.
We are going to use the CDN option to do this. We need a simple input element to let Typeahead show it’s magic on. Our HTML file will look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel and Typeahead Tutorial</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<h1>Laravel and Typeahead Tutorial</h1>
<hr>
<form class="typeahead" role="search">
<div class="form-group">
<input type="search" name="q" class="form-control" placeholder="Search" autocomplete="off">
</div>
</form>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins and Typeahead) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Bootstrap JS -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<!-- Typeahead.js Bundle -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"></script>
</body>
</html>
To initialize Typeahead on any input field use:
$(".search-input").typeahead(options, [*datasets])
Note:: Set autocomplete="off"
for the input box if you want to prevent default browser menus from appearing.
When initializing a typeahead, there are a number of options you can configure. Some of them are:
{{classNames.highlight}}
. Defaults to false
.true
.1
.You can view them all here.
Our options look like this:
$("#navbar-search-input").typeahead({
hint: true,
highlight: true,
minLength: 1
});
A typeahead can have many datasets. Take the example of the Facebook search. When you type something, it returns many suggestions in groups like users, groups, pages, games, etc.
Here we will be only returning our users as the dataset from the server.
Datasets can be configured using many options. Some of them are following:
{{classNames.dataset}}
- to form the class name of the containing DOM element. Must only consist of underscores, dashes, letters (a-z), and numbers. Defaults to a random number.source
can also be a Bloodhound instance. Required.View them all here
For the source, we will fetch the data from the server, To do so, we will use Bloodhound. It is typeahead.js
suggestion engine. It offers many advanced functionalities including caching and fetching remote data.
// Set the Options for "Bloodhound" suggestion engine
var engine = new Bloodhound({
remote: {
url: '/find?q=%QUERY%',
wildcard: '%QUERY%'
},
datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
queryTokenizer: Bloodhound.tokenizers.whitespace
});
We have not setup the route yet as we have specified in the value of URL, but we will do that later. datumTokenizer
needs an array of JSON, which, in this example is 'q'
.
Now that, we have the data, we can use it as the source value of typeahead like:
source: engine.ttAdapter()
Typeahead allows the use of templates to modify the styling of the suggestions. As we have pulled in Bootstrap, we can use the list-group classes for the styling.
templates: {
empty: [
'<div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>'
],
header: [
'<div class="list-group search-results-dropdown">'
],
suggestion: function (data) {
return '<a href="' + data.profile.username + '" class="list-group-item">' + data.name + ' - @' + data.profile.username + '</a>'
}
}
After all, your code should look like this:
jQuery(document).ready(function($) {
// Set the Options for "Bloodhound" suggestion engine
var engine = new Bloodhound({
remote: {
url: '/find?q=%QUERY%',
wildcard: '%QUERY%'
},
datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
queryTokenizer: Bloodhound.tokenizers.whitespace
});
$(".search-input").typeahead({
hint: true,
highlight: true,
minLength: 1
}, {
source: engine.ttAdapter(),
// This will be appended to "tt-dataset-" to form the class name of the suggestion menu.
name: 'usersList',
// the key from the array we want to display (name,id,email,etc...)
templates: {
empty: [
'<div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>'
],
header: [
'<div class="list-group search-results-dropdown">'
],
suggestion: function (data) {
return '<a href="' + data.profile.username + '" class="list-group-item">' + data.name + '- @' + data.profile.username + '</a>'
}
}
});
});
To get started on the backend side, install a new Laravel application using Composer.
- composer create-project --prefer-dist laravel/laravel app-name
You can do any other configuration you need.
For the search function, we will be using Searchable. This package provides a trait that adds a simple search function to Eloquent Models. Searchable allows you to perform searches in a table giving priorities to each field for the table and its relations.
Go ahead and install it by running this command:
- composer require "nicolaslopezj/searchable:1.*"
Add the trait to the model, you want the search function to be available on You also need to specify the search rules(the columns you want to run the search on) via the $searchable
property.
<?php
namespace App;
use App\Profile;
use Nicolaslopezj\Searchable\SearchableTrait;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use SearchableTrait;
protected $searchable = [
'columns' => [
'users.name' => 10,
'profiles.username' => 5,
'profiles.bio' => 3,
'profiles.country' => 2,
'profiles.city' => 1,
],
'joins' => [
'profiles' => ['users.id','profiles.user_id'],
],
];
public function profile()
{
return $this->hasOne(Profile::class);
}
}
Now, you can search the User model with the search function like this:
// Simple search
$users = User::search($query)->get();
// Search and get relations
// It will not get the relations if you don't do this
$users = User::search($query)
->with('profile')
->get();
We will have one route for returning JSON output to the Bloodhound query. Add this route to your routes.php
file.
Route::get('find', 'SearchController@find');
As mentioned in the route, we will need a controller calledSearchController
with a method ‘query’ on it. Run this command to make this controller:
- php artisan make:controller SearchController
After that, add this method to the controller:
public function find(Request $request)
{
return User::search($request->get('q'))->with('profile')->get();
}
As we simply return the data back, by default Laravel casts that to JSON.
We know that by default Laravel includes a route and a view for displaying the homepage of your site.
Route::get('/', function () {
return view('welcome');
});
To connect everything, we can just modify that view to include what we have built above. Your final view should look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel and Typeahead Tutorial</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<h1>Laravel and Typeahead Tutorial</h1>
<hr>
<form class="typeahead" role="search">
<div class="form-group">
<input type="search" name="q" class="form-control search-input" placeholder="Search" autocomplete="off">
</div>
</form>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins and Typeahead) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Bootstrap JS -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<!-- Typeahead.js Bundle -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"></script>
<!-- Typeahead Initialization -->
<script>
jQuery(document).ready(function($) {
// Set the Options for "Bloodhound" suggestion engine
var engine = new Bloodhound({
remote: {
url: '/find?q=%QUERY%',
wildcard: '%QUERY%'
},
datumTokenizer: Bloodhound.tokenizers.whitespace('q'),
queryTokenizer: Bloodhound.tokenizers.whitespace
});
$(".search-input").typeahead({
hint: true,
highlight: true,
minLength: 1
}, {
source: engine.ttAdapter(),
// This will be appended to "tt-dataset-" to form the class name of the suggestion menu.
name: 'usersList',
// the key from the array we want to display (name,id,email,etc...)
templates: {
empty: [
'<div class="list-group search-results-dropdown"><div class="list-group-item">Nothing found.</div></div>'
],
header: [
'<div class="list-group search-results-dropdown">'
],
suggestion: function (data) {
return '<a href="' + data.profile.username + '" class="list-group-item">' + data.name + ' - @' + data.profile.username + '</a>'
}
}
});
});
</script>
</body>
</html>
In this tutorial, we have implemented smart search using Laravel and Typeahead. Thanks to the awesome library: Typeahead, and the awesome framework: Laravel, for making this all a lot easier.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.