Shout! Shout!
Let it all out!
These are the things I can do with Scout!

Hi, I'm Steven Maguire

  • I've been building software since 2004.
  • Contribute to open source.
  • Author courses for
  • VP, Technology at Earth Class Mail.
  • Tweet from @stevenmaguire.

Search is hard.

Basic SQL

select * from table where column like '%keyword%';

Basic-ish SQL

    column_one LIKE '%keyword%'
    OR column_two LIKE '%keyword%'

What about ranking by relevance?

This isn't a SQL talk.

Luckily other people solved this problem.


  • They do the work
  • HTTP API (mostly)
  • Scale with growth
  • Reliable (mostly)


  • You do the work to
    keep records fresh
  • Non-customizable (mostly)
  • Expensive to scale
  • Unreliable (sometimes)

We can't address all disadvantages.

Except one!

You do the work to
keep records fresh!

Laravel + Search = :)


Laravel Scout provides a simple, driver based solution for adding full-text search to your Eloquent models.


  • Listen for CRUD events on models then "upsert" or remove documents in third-party search service
  • Search models using keyword, fetch results from third-party search service


  • Not compatible for Laravel < 5.3
  • Scout is a separate package
  • Third-party search drivers are extendable,
    may require separate packages
  • Syncing can be synchronous or asynchronous using queues
  • Model relationships are not very well supported
  • Speed varies service to service

Let's build something!

I'm a fan of mascots.

and films by
Christopher Guest.

As a fan, I want a great experience when researching mascots.

We can help!

$ composer create-project --prefer-dist laravel/laravel mascots
$ cd mascots

New Laravel Project

$ php artisan make:model Mascot --migration
Model created successfully.
Created Migration: 2016_09_25_181801_create_mascots_table

New Laravel Model



public function up()
    Schema::create('mascots', function (Blueprint $table) {

public function down()

Define Migration


$ php artisan migrate
Migration table created successfully.
Migrated: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrated: 2016_09_25_181801_create_mascots_table

Run Migration



Route::resource('mascots', 'MascotController');

Create Resource Route


$ php artisan make:controller MascotController --resource
Controller created successfully.

Create Resource Controller

Let's add Scout



    'providers' => [

Include Scout Service Provider


$ composer require laravel/scout

Include Scout Package


namespace App;

use Laravel\Scout\Searchable;
use Illuminate\Database\Eloquent\Model;

class Mascot extends Model
    use Searchable;

Add Searchable Trait to Model


$ php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

Publish Scout Config


return [
    'driver' => env('SCOUT_DRIVER', 'algolia'),
    'queue' => false,
    'algolia' => [
        'id' => env('ALGOLIA_APP_ID', ''),
        'secret' => env('ALGOLIA_SECRET', ''),

Update Configuration


Create an Algolia App

$ composer require algolia/algoliasearch-client-php

Include Algolia Package


Include Algolia API Keys


Indexing Scenarios

Batch import all existing models

$ php artisan scout:import "App\Mascot"

Add new records


$mascot = new App\Mascot;

// ...


Conditionally add records for existing models


// Adding via Eloquent query...
App\Mascot::where('popularity', '>', 10)->searchable();

// You may also add records via relationships...

// You may also add records via collections...

Update records


$mascot = App\Mascot::find(1);

// ...


Remove records


$mascot = App\Mascot::find(1);

// ...


Conditionally remove records


// Removing via Eloquent query...
App\Mascot::where('popularity', '>', 10)->unsearchable();

// You may also remove via relationships...

// You may also remove via collections...

Skip/pause syncing


App\Mascot::withoutSyncingToSearch(function () {
    // Perform model actions without fear 
    // of syncing data with third-party 
    // search service

Searching Scenarios



$mascots = App\Mascot::search('cereal')->get();

Performs search against third-party search service, then queries database for all models associated with results; returns Collection.

local.INFO: select * from `mascots` where `id` in ('2', '1') 

Limiting Results


$mascots = App\Mascot::search('cereal')
    ->where('popularity', 10)

Currently, these clauses only support basic numeric equality checks, and are primarily useful for scoping search queries by a tenant ID. Since a search index is not a relational database, more advanced "where" clauses are not currently supported.



$mascots = App\Mascot::search('cereal')->paginate();

// Specific per page

$mascots = App\Mascot::search('cereal')->paginate(10);


$ composer require laravel/passport
$ php artisan migrate
Migrated: 2016_06_01_000001_create_oauth_auth_codes_table
Migrated: 2016_06_01_000002_create_oauth_access_tokens_table
Migrated: 2016_06_01_000003_create_oauth_refresh_tokens_table
Migrated: 2016_06_01_000004_create_oauth_clients_table
Migrated: 2016_06_01_000005_create_oauth_personal_access_clients_table
$ php artisan passport:install
Encryption keys generated successfully.
Personal access client created successfully.
Password grant client created successfully.

Include Passport

Performance Tip

Use a queue!


return [
    'queue' => true,


More Tips

  • Consider leveraging search service conditionally,
    don't use for every look up operation. Services
    cost $$$.
  • The solution does not defer load from the database.
  • Usage looks similar to Eloquent; it's different!
  • Use a queue to defer syncing tasks.
  • Experiment; not for everyone.



Thank you!

on twitter
on electronic mail

on github

Shout! Shout! Let it all out! These are the things I can do with Scout!

By Steven Maguire

Shout! Shout! Let it all out! These are the things I can do with Scout!

Deck originally created for a presentation to a gathering of the Chicago Laravel Meetup group -

  • 3,213