sti3bas/laravel-scout-array-driver
An in-memory array driver for Laravel Scout that lets you use Scout's search API without an external engine. Ideal for tests and small datasets, it indexes models in a PHP array and supports where clauses, pagination, and ordering—keeping your suite fast and dependency-free.
The ArrayEngine is an in-memory Scout driver: it lets search() calls run against a plain PHP array instead of Meilisearch/Algolia/Typesense. That makes it a test-time and local-dev tool, not a production engine.
composer require laravel/scout:^11
composer require --dev <engine-package>:^5.0
phpunit.xml so it applies before the app boots:
<env name="SCOUT_DRIVER" value="array"/>
For local-only use, register the driver in config/scout.php:
'array' => ['driver' => 'array'],
public function test_search_returns_published_posts(): void
{
Post::factory()->create(['title' => 'Laravel queues', 'status' => 'published']);
Post::factory()->create(['title' => 'Laravel queues', 'status' => 'draft']);
$this->get('/search?q=queues')
->assertOk()
->assertJsonCount(1, 'data');
}
Where to look first: the ArrayEngine class itself (update(), delete(), search()), the service provider's Scout::extend('array', ...) registration, and the engine's handling of the builder's where/orderBy clause arrays — that last one is exactly what #34 touched.
Test isolation over mocking. Prefer the array driver to hand-rolled Scout::shouldReceive(...) mocks. Because Searchable models index on saved, a factory create is enough to make a record findable — no explicit indexing step.
Filtering and sorting now behave like Scout 11. The v5 fix means the engine reads the same clause shape Scout 11's Builder emits, so this works as written:
Post::search('queues')
->where('status', 'published')
->whereIn('tag', ['php', 'laravel'])
->orderBy('published_at', 'desc')
->paginate(15);
Treat this as a contract test on your query shape (does the controller build the right builder?), not on relevance.
Trait hooks still apply. searchableAs(), shouldBeSearchable(), and toSearchableArray() are honored on write, so you can test that draft records never enter the index without touching a real engine.
Local dev fallback. Set SCOUT_DRIVER=array in .env.local / a non-production env so sail up-style workflows don't require Meilisearch. Combine with Searchable::withoutSyncingToSearch(fn () => ...) in seeders when the index volume is large and irrelevant to the page you're building.
Custom engines. If you need extra behavior (custom operators, relevance stubs), extend ArrayEngine and re-register the driver in a service provider rather than editing vendor code:
Scout::extend('array', fn ($app) => new CustomArrayEngine($app['config']));
Assertions worth writing. Result IDs and ordering, pagination totals (total()), and that a deleted model disappears ($post->delete() then Post::search('queues')->get() is empty).
where/orderBy builder format. On Scout 10 the engine will silently drop or misread clauses, so pin v4 if you cannot upgrade Scout. This is the single most likely upgrade failure.php artisan scout:import and scout:flush against the array driver write to memory that dies with the command. Do not use it to "populate" anything, and do not expect data to survive across requests under PHP-FPM or Octane. Index-then-query must happen in the same test method.status fields) rather than assuming LIKE-style behavior.phpunit.xml sets SCOUT_DRIVER but a cached config.php exists locally, the env var is ignored. Run php artisan config:clear when a driver switch "doesn't take".config('scout.driver') inside the test to confirm the driver, and Post::search('x')->toArray() — no wait, use ->get()->pluck('id') — to see what the builder actually produced. If clauses vanish, you are almost certainly on the wrong Scout/engine major pair.How can I help you explore Laravel packages today?