mozex/laravel-scout-bulk-actions
Laravel Scout's commands handle one model at a time. This package auto-discovers every model using the Searchable trait and lets you import, flush, or refresh them all with a single command, plus queued bulk imports for faster indexing.
Architecture fit. This is a thin orchestration layer, not a runtime library. It adds one service provider and a set of Artisan commands; there is no footprint in the request path, no model trait to add, no schema change, no service binding, and no event hooks. That makes it architecturally cheap to adopt and equally cheap to remove — a meaningful de-risking property for a TPM, because the exit cost is one composer remove plus deleting a config file.
The mechanism is straightforward and inspectable: Symfony Finder walks model_directories, file paths are converted to FQCNs, Reflection confirms each class is a concrete, non-abstract Eloquent model, the Searchable trait is verified, and Scout's native scout:import / scout:flush / scout:queue-import is then invoked per model behind a progress bar. Because it delegates rather than reimplements, engine behavior (Algolia, Meilisearch, Typesense, database, collection drivers) is inherited from Scout — the package stays engine-agnostic and Scout upgrades carry most of the compatibility burden.
Fit is strongest in two scenarios: applications with roughly ten or more searchable models where per-model scout:import calls have become a deploy-script ritual, and platform teams that currently own a hand-rolled version of exactly this wrapper. Below ~3 searchable models the package adds a dependency to solve a problem that doesn't exist yet.
Integration feasibility. Low. composer require, auto-discovery, and a single published config file whose only real decision is model_directories. No application code changes. A developer can validate the discovery set in an afternoon on a staging branch.
Technical risk.
scout:refresh that means a partially flushed and partially imported index with no checkpoint, no per-model isolation, and no restart-from-here. Recovery is a full re-run, which for large datasets may be hours.Searchable trait directly are detected. Models inheriting searchability from a base class, or living outside configured directories, will be skipped — and the command still exits 0. A green pipeline with a stale index is the worst failure class here, because nothing signals it.scout:flush-all and the no-argument scout:refresh are full search-outage operations. --fresh narrows the gap to per-model import duration, which is a genuine improvement, but the package offers no alias-swap or shadow-index rebuild. Any zero-downtime search requirement must be solved at the engine layer, outside this package's scope.--dry-run, no JSON summary, no structured logging. CI and orchestration wrappers will need to be written in-house.Key questions.
Searchable through a base class or trait composition rather than declaring it directly?app/Models, or do we need glob patterns for a modular/domain layout?laravel/scout at ^10.18 or ^11? Version 1.8.0's --fresh and --order depend on the lower bound.Stack fit. Requires PHP 8.2+, Laravel 11–13, and laravel/scout ^10.18 || ^11. Version 1.3.1 dropped PHP 8.1 and Laravel 10, so any application still on Laravel 10 must pin to ^1.2.4 and forgo --fresh, --order, and the queued bulk command (introduced in 1.5.0). This is a real fork in the support matrix worth confirming before scoping.
Because the package shells out to Scout rather than talking to an engine, it is driver-agnostic — the same integration works for Algolia, Meilisearch, Typesense, and the database driver. That is a genuine compatibility asset and means engine choice does not gate adoption.
Migration path. Three viable shapes, ordered by effort:
bin/reindex.sh collapse into one scout:refresh --force. Lowest risk, immediate payoff.No data migration is involved; index contents are reconstructed from the database. Rollback is reverting the deploy script and removing the package.
Compatibility considerations. Config caching (config:clear after changing model_directories in production) is a documented gotcha that will bite in cached-config pipelines. Multi-tenant applications get no tenant scoping — a bulk import indexes every tenant at once, which may be fine or may be a rate-limit problem. Octane is irrelevant since everything is CLI. scout:queue-import-all depends on your queue worker topology; the --queue=indexing option lets you isolate indexing from user-facing jobs, which should be used.
Sequencing.
Searchable usage, confirm directory coverage).composer require, publish config, run scout:flush-all and scout:import-all against a non-production index. Confirm the discovered model list matches the inventory.scout:refresh against a staging index of production-like volume; measure wall-clock time, memory, and engine throttling behavior. This produces the numbers that justify or kill the queued path.--force to any non-interactive context, since every command prompts for confirmation under APP_ENV=production. Wrap the command to capture exit codes and emit a structured summary, compensating for the missing --dry-run.scout:queue-import-all --queue=indexing --order=desc once volume justifies it, with monitoring on queue depth and job failure rate.Maintenance. Small dependency surface (Symfony Finder, laravel/prompts, Scout) and a maintainer who ships regularly — fourteen tagged releases with dependabot-driven version bumps. Composer updates should be routine, though the laravel/prompts constraint has churned across three major ranges (0.1 → 0.2 → 0.3), which historically caused a patch release (1.2.3 "Fix laravel/prompts dependency"). Budget for occasional transitive-constraint friction. The package should be pinned to a minor version range and upgraded deliberately, not floated.
Support. No SLA, no commercial backing, one maintainer, sponsor-funded. Security reports go to a personal email rather than a private advisory process. For a package on the deploy path this is acceptable because the blast radius is bounded and MIT + small code means you can fork in a day, but it should be an explicit, recorded acceptance rather than an oversight. The changelog discipline substantially reduces triage cost when something breaks.
Scaling. Two regimes. Synchronous commands run in a single PHP process: wall-clock time scales with total row count across all models, memory is bounded by chunk size but iteration is serial, and the command holds the terminal for the duration. That is fine to roughly a few hundred thousand documents and painful beyond it. The queued path (scout:queue-import-all) splits each model by ID range and dispatches jobs, so throughput scales horizontally with worker count and the operator is decoupled from the run. --order=desc is a genuinely thoughtful detail: on a multi-hour rebuild it puts recent content back into search first instead of last, which materially improves perceived quality during a long reindex.
The unaddressed scaling dimension is the search engine itself — chunk sizes, queue concurrency, and engine rate limits are three dials that interact, and the package only exposes the first.
Failure modes. Ranked by operational severity:
refresh leaves some models flushed and unimported. Mitigation: run --fresh per model instead of a global flush, keep a pre-run snapshot, and treat any non-zero exit as "search is degraded" in the incident runbook.model_directories is invisible and the command exits 0. Mitigation: assert the discovered model count against a hard-coded expected list in CI.--force, production runs block on an interactive prompt and hang the pipeline. Trivial but a classic.model_directories without config:clear silently scans the old paths.--fresh minimizes but does not eliminate it.Ramp-up. Near zero. Any Laravel developer understands php artisan scout:import-all --force from the command name alone, and the README is unusually good — options, the distinction between --fresh and refresh, the --order semantics, and production-safety behavior are all documented with rationale rather than just syntax. The Laravel Boost skill reduces AI-assistant hallucination risk during onboarding, including flagging the direct-trait-use pitfall.
The realistic ramp-up cost is not learning the package; it is building the surrounding discipline the package doesn't provide — a model inventory assertion in CI, a captured exit-code wrapper, and an incident runbook entry for "index is partial." Budget a day for adoption and a sprint for the guardrails, and treat the guardrails as the actual deliverable.
How can I help you explore Laravel packages today?