Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Scout Mysql Driver Laravel Package

yab/laravel-scout-mysql-driver

A Laravel Scout engine that powers full-text search directly with MySQL, so you can skip Elasticsearch or Algolia. Adds relevance ranking, boolean and wildcard queries, and easy model integration using native MySQL indexes.

View on GitHub
Deep Wiki
Context7

Getting Started

The shortest path from zero to a working search box:

  1. Install Scout and the driver:
    composer require laravel/scout yab/laravel-scout-mysql-driver
    
  2. Set the driver in .env:
    SCOUT_DRIVER=mysql
    
    Publish config/scout.php (php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider") if you need to tweak the mysql block — the package registers itself through Scout's extend() mechanism, so the driver name is usually enough.
  3. Add a FULLTEXT index. This is the step people forget; without it, MATCH ... AGAINST returns nothing and MySQL may even error on some versions:
    Schema::table('posts', function (Blueprint $table) {
        $table->fullText(['title', 'body']);
    });
    
  4. Make the model searchable and tell the driver which columns form the index:
    use Laravel\Scout\Searchable;
    
    class Post extends Model
    {
        use Searchable;
    
        protected $searchableColumns = ['title', 'body'];
    }
    
  5. First use case — a blog/listing search that doesn't justify Meilisearch or Algolia:
    Post::search('laravel scout')->paginate(15);
    

Where to look first: the mysql block in config/scout.php, your model's $searchableColumns, the migration holding the FULLTEXT index, and finally vendor/yab/laravel-scout-mysql-driver/src/Engines/MysqlEngine.php — the engine is small and reading it once explains every behavior below.

Implementation Patterns

Search plus Eloquent constraints. The engine compiles search down to a whereRaw on the model's own query builder, so normal Eloquent chaining works and is the main reason to use this package:

Post::search($term)
    ->where('status', 'published')
    ->where('tenant_id', $tenantId)
    ->orderByDesc('published_at')
    ->paginate(20);

Soft deletes, global scopes, and eager loads behave as usual because you're querying the real table.

No index to synchronize. Because search runs directly against the table, Scout's searchable(), unsearchable(), and scout:import are effectively no-ops. Writes are instantly searchable — a genuine advantage over external engines, and it removes the "why is my record not in the index" class of bugs entirely.

Boolean mode for precision. For user-driven queries, boolean syntax gives you +required, -excluded, "exact phrase", and prefix*. Sanitize raw user input before passing it through, or wrap it in a small query-builder service so operators like @ or unbalanced quotes don't produce SQL errors.

Column customization per model. If a model exposes more than title/body, align $searchableColumns exactly with the migration's fullText([...]) column list. A mismatch is the most common silent failure: MySQL requires the MATCH column set to correspond to an existing FULLTEXT index.

Per-tenant or per-scope indexes. For multi-tenant apps, avoid one giant index. Either scope with where('tenant_id', ...) (simplest, still uses the index for the match and filters afterwards) or maintain separate searchable tables if data volumes justify it.

Customizing behavior. Subclass the engine and re-register it if you need relevance ordering, result mapping, or query preprocessing:

Scout::extend('mysql', fn () => new App\Search\CustomMysqlEngine());

This is the cleanest extension point given the package is archived.

Testing. Use MySQL (or MariaDB) in CI for anything touching search; SQLite has no MATCH ... AGAINST. For unit tests, fake the engine or assert on the generated SQL rather than on relevance ordering.

Gotchas and Tips

  • The package is archived (last release Dec 2021). Pin it, check composer.json constraints against your Laravel/PHP version, and be prepared to fork or add a replace entry. It predates Laravel 10/11 conventions; expect to loosen constraints rather than get upstream fixes.
  • MySQL version matters. InnoDB FULLTEXT needs 5.6+; 5.7+ is the realistic baseline. MariaDB's implementation differs subtly — test before assuming parity.
  • Short words silently vanish. innodb_ft_min_token_size defaults to 3, so "go", "UI", "C#" won't match. Lower it in my.cnf (requires index rebuild) if short tokens matter.
  • Stopwords. Common words are dropped by the default stopword list. If a term is meaningful to your domain, customize innodb_ft_server_stopword_table — you cannot fix this from application code.
  • Natural-language mode has a 50% threshold on MyISAM (terms appearing in more than half the rows are ignored). InnoDB doesn't apply that rule, but relevance can still be unintuitive on small tables — prefer boolean mode for deterministic results.
  • No substring, no typo tolerance, no stemming, no faceting, no highlighting. This is a keyword matcher, not a search engine. If users expect "larval" to find "laravel", this is the wrong tool.
  • Relevance ordering isn't automatic. The engine builds a WHERE clause; if you need "best match first", add orderByRaw("MATCH (...) AGAINST (? IN BOOLEAN MODE) DESC", [...]) yourself.
  • FULLTEXT index changes require a migration and a table rebuild. Adding a column to $searchableColumns without a matching fullText migration breaks search. Keep the two in lockstep.
  • Reserved words and prefixes. If a searchable column name collides with a reserved word or your app uses a table prefix, check the raw SQL the engine generates with ->toSql() before debugging further.
  • Config caching. After changing SCOUT_DRIVER or the mysql block, run php artisan config:clear; a stale cached config is a frequent "driver not found" cause.
  • Empty queries. Decide explicitly what Post::search('') should do — return everything, or short-circuit. Behavior varies across Scout versions and can surprise you in paginated listings.
  • Scout API coverage is partial. Methods like within(), keys(), and raw engine access may not be implemented. Anything you rely on beyond search/where/paginate/take deserves a test against the actual driver.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib