How a Rejected PR Became Laravel Scout Bulk Actions
Package
mozex/laravel-scout-bulk-actions
Import, flush, and queue-import all your Laravel Scout searchable models at once. Auto-discovers models, runs in bulk, tracks progress.
If you've used Laravel Scout on a project with more than a couple of searchable models, you know the drill. You change something in your toSearchableArray(), and now you need to flush and reimport every model's index. Two commands per model. Manually. One at a time.
On a project with ten searchable models, that's twenty commands to rebuild your search indexes.
I ran into this constantly during local development. I'd tweak a Meilisearch index setting, then sit there typing the same pair of commands over and over. Flush Post, import Post. Flush Product, import Product. Flush Order, import Order. You get the idea.
It wasn't just tedious. It was the kind of friction that made me avoid touching index configurations altogether. I'd put off restructuring my searchable data because I didn't want to deal with the manual reset dance afterward.
The Pull Request
In May 2022, I opened PR #624 on the Laravel Scout repository. The idea: let scout:import work without arguments. Skip the model name, and it auto-discovers every class using the Searchable trait and imports them all.
Taylor Otwell's response was fair:
To preserve our ability to adequately maintain the framework, we need to be very careful regarding the amount of code we include. If possible, please consider releasing your code as a package so that the community can still take advantage of your contributions!
He was right. Adding auto-discovery to a first-party package means maintaining directory scanning logic, class name resolution, abstract class filtering, and edge case handling across different project structures. That's real complexity for a feature not every Scout user needs.
So I did exactly what he suggested.
Building the Package
Laravel Scout Bulk Actions shipped in mid-2023. The core idea stayed the same: auto-discover models, run commands in bulk. But as a standalone package, I could go further than what made sense inside Scout itself.
Install it:
composer require mozex/laravel-scout-bulk-actions
That's it. No service provider registration, no config publishing. Laravel's package discovery handles the rest.
The Commands
Four commands, each mirroring a Scout operation but applied across all your searchable models.
Import everything:
php artisan scout:import-all
Clear all indexes:
php artisan scout:flush-all
Flush and reimport in one step (the one I use most during development):
php artisan scout:refresh
Queued bulk import for large datasets:
php artisan scout:queue-import-all --chunk=500 --queue=indexing
That last command dispatches jobs splitting records by ID range, the same approach Scout's native scout:queue-import uses. Except it runs across every model. If you're rebuilding indexes for millions of rows, this is the fastest path.
All commands ask for confirmation in production. Pass --force for CI pipelines and automated deployments.
How Model Discovery Works
By default, the package scans app/Models. For most Laravel projects, that covers everything. But you can point it anywhere:
// config/scout-bulk-actions.php
return [
'model_directories' => [
app_path('Models'),
base_path('Modules/*/Models'),
],
];
Glob patterns work. That Modules/*/Models entry covers every module's models directory in a modular architecture.
Under the hood, Symfony's Finder locates PHP files. Then Reflection checks each one: is it a concrete class? An Eloquent model? Does it use the Searchable trait? Only models passing all three get processed.
30K+ Downloads, Zero Promotion
The package passed 30,000 installs on Packagist without a single promotional effort. No blog posts (until now), no tweets, no conference talks. Every download came from developers searching Packagist or Google for a solution to the same problem I had.
That says something about the pain point. Running individual Scout commands per model is a universal frustration for anyone with more than a few searchable models. Enough developers agreed to install a package to fix it.
It works with any Scout driver, by the way. Meilisearch, Algolia, Typesense, the database driver, whatever custom implementation you're running. The package doesn't touch the driver layer. It discovers your models and delegates to Scout's own commands.
Why the Rejection Was Worth It
Sometimes a rejected PR is the best outcome. If Taylor had merged my changes into Scout, the feature would have been constrained by what fits inside a first-party package. As a standalone package, I added queued bulk imports, configurable directory scanning with glob support, and the refresh command that combines flush and import.
The rejection wasn't a "no." It was a "not here."
And "not here" turned into something better for everyone. I use this package on every project where I touch Scout. Zero hesitation. composer require and move on.
If you maintain a Laravel project with Scout, give it a try. One install and you'll never manually import models one by one again.
Stay in the Loop
Get the latest posts delivered to your inbox - on your schedule.