libre-informatique/seed-batch-bundle
A Symfony/Sylius bundle that adds configurable batch actions to admin grids, letting editors select multiple resources and run bulk operations directly from the grid. It provides reusable batch action services and UI controls to streamline repetitive admin workflows.
Reality check first. libre-informatique/seed-batch-bundle is a Symfony bundle, and it is marked [DEPRECATED]. There is no ServiceProvider, no auto-discovery, no php artisan vendor:publish path, and the composer.json will constrain symfony/* packages you do not have. Do not attempt composer require inside a Laravel app — resolve the dependency tree in a throwaway Symfony skeleton if you must install it at all, or simply clone the repo and read it.
Also read the name correctly: "Seed" is the host application (Libre Informatique's admin/ERP line), and "Batch" means batch processing — a framework for mass actions over entity collections, not a seed-lot/agriculture data model. The other evaluations in this repo got that backwards. What you are mining here is a pluggable batch-action architecture, not agritech schema.
Read in this order:
composer.json — confirm the abandoned flag, PHP floor, and symfony/* / Doctrine constraints.*Bundle.php and DependencyInjection/ — see how actions are registered and tagged. This is the core idea worth stealing.Resources/config/ — the service tag name and how the registry collects tagged services.Controller/ and any admin/grid glue — last, and only to see how the UI invokes a batch.First use case: you have an admin grid (Filament, Nova, Livewire, Backpack) where an operator selects 500–50,000 rows and clicks "Export", "Reassign", or "Recalculate". You want that logic out of the controller, out of the Livewire component, and off the web request. That is the exact problem this bundle solves in Symfony — and the exact shape of Bus::batch() + chunked jobs in Laravel. Read this repo to validate your design, then build it natively.
Translate the tagged-service registry to a Laravel registry. Symfony's tagged services exist so that adding a batch action is one class plus one tag, with no edits to a central switch statement. In Laravel, put that class list in a config key and resolve it through a small registry:
// config/batch.php
return ['actions' => [
'export' => App\Batch\Actions\ExportEntities::class,
'recalculate' => App\Batch\Actions\RecalculateTotals::class,
]];
final class BatchActionRegistry
{
public function __construct(private array $actions) {}
public function resolve(string $key): BatchAction
{
return app($this->actions[$key] ?? throw new InvalidArgumentException("Unknown batch action [$key]"));
}
public function all(): array { /* ->map(fn($c) => app($c)) for the UI dropdown */ }
}
The UI reads all() to build the action list; the controller takes a key and a query/selection, then hands off. New action = one class + one config line.
Keep the contract chunk-shaped, not row-shaped. The Symfony version almost certainly passes entities one at a time because it runs inline. In Laravel you want queue safety, so define:
interface BatchAction
{
public function key(): string;
public function label(): string;
/** @param Collection<int, Model> $models */
public function handle(Collection $models, BatchActionContext $ctx): void;
}
Then one job per chunk, dispatched through Bus::batch() so you get then / catch / finally callbacks and per-chunk failure tolerance (allowFailures()) for free:
$ids = $query->pluck('id'); // snapshot the selection up front
$jobs = $ids->chunk(500)->map(
fn ($chunk) => new RunBatchActionJob($actionKey, $chunk->all(), $contextId)
);
Bus::batch($jobs)
->allowFailures()
->onQueue('batch')
->name("{$actionKey}:{$contextId}")
->dispatch();
Snapshot the selection. The bundle operates on the ids the grid submitted. Do the same — never re-run the user's where clause inside the worker, or the set mutates under you mid-run.
Persist per-run state. A batch_runs table (id, action, total, processed, failed, payload, started_at, finished_at) plus a batch_run_failures table gives operators a progress bar and gives you a debugging trail. The bundle relies on the host app for this; you will not.
Where to put the code. app/Batch/{Actions,Jobs,BatchActionRegistry.php} with a bound BatchActionContext carrying actor id, run id, and action options. Controllers/Livewire components shrink to: authorize, validate, resolve action, create batch_runs row, dispatch, redirect with a run id.
NOASSERTION license. The repo's license file does not map to an SPDX identifier. Do not copy code, mapping files, or class bodies into your app until someone clears it. Read it as documentation; write your own implementation from the pattern.chunk() with skip/take degrades and shifts rows when the action mutates the filtered column. Use chunkById() or lazyById().whereNull('batched_at') predicate or a unique job — a partial re-run is worse than a clean failure.allowFailures() cuts both ways. It keeps one bad row from killing the batch, but you must record the failure yourself in catch; otherwise the operator sees "done" with silent losses.match statement in a controller to add an action, the abstraction has already failed.Horizon/failed_jobs) first, then batch_runs.processed vs. total. A mismatch with no failed jobs almost always means a job timeout, not a logic bug.How can I help you explore Laravel packages today?