Implementing Full-Text Search with Laravel Scout

The Problem with Manual Database Queries

Standard database queries fail when your data scales. You might start with a simple where('title', 'like', "%$query%") clause, but requirements quickly snowball. Soon, you need to search descriptions, genres, and metadata. Coding these chainable queries manually creates bloated, unreadable controllers. Performance hits become inevitable as your database struggles to scan every string across multiple columns.

solves this by providing a driver-based solution that offloads the heavy lifting to dedicated search engines.

Prerequisites and Setup

Before you begin, ensure you have a

project running with a functional database. You should understand
Eloquent
models and basic controller logic. To follow this guide, you will need the Scout package installed via Composer and a search driver like
Algolia
,
Meilisearch
, or
Typesense
configured in your environment variables.

Making Models Searchable

To enable search, you must prepare your model. Add the Searchable trait to your class definition. This trait hooks into Eloquent's model observers to keep your search index synced with your database.

namespace App\Models;

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

class Movie extends Model
{
    use Searchable;

    public function toSearchableArray(): array
    {
        return [
            'title' => $this->title,
            'description' => $this->description,
            'rank' => $this->rank,
        ];
    }
}

The toSearchableArray method is your control center. It defines exactly what data gets sent to the search engine. This prevents sensitive data from leaking into your public index and allows you to format relationships or calculated fields for better search relevance.

Clean Controller Integration

Once the model is ready, your controller code shrinks significantly. You can replace messy query builders with a single, expressive search method.

public function index(Request $request)
{
    $movies = Movie::search($request->input('search'))->get();
    return view('movies.index', compact('movies'));
}

Syncing Data and Using External Drivers

While the local database driver works for development, production environments require specialized tools. Services like

provide lightning-fast results and handle complex relationship searches that standard SQL struggles with. To populate your external index for the first time, use the artisan command:

php artisan scout:import "App\Models\Movie"

Tips and Gotchas

Don't try to search relationships directly using the basic database driver; it will look for a column that doesn't exist on the table and throw an error. If you need to search through related data like Genres, use a more advanced driver. Always remember that Scout uses observers—if you use mass updates via Movie::query()->update(), the search index won't see those changes. Use standard Eloquent save() or update() calls to keep everything in sync.

3 min read