# How a Rejected PR Became Laravel Scout Bulk Actions

**Author:** Mozex | **Published:** 2026-04-03 | **Tags:** Laravel, PHP, Open Source | **Package:** mozex/laravel-scout-bulk-actions | **URL:** https://mozex.dev/blog/10-how-a-rejected-pr-became-laravel-scout-bulk-actions

---


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.

<!--more-->

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](https://github.com/laravel/scout/pull/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](https://github.com/mozex/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:

```bash
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:**

```bash
php artisan scout:import-all
```

**Clear all indexes:**

```bash
php artisan scout:flush-all
```

**Flush and reimport in one step** (the one I use most during development):

```bash
php artisan scout:refresh
```

**Queued bulk import for large datasets:**

```bash
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:

```php
// 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](https://packagist.org/packages/mozex/laravel-scout-bulk-actions) 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](https://github.com/mozex/laravel-scout-bulk-actions). One install and you'll never manually import models one by one again.