tourze/symfony-schedule-entity-clean-bundle
A Symfony bundle for scheduling automatic cleanup of Doctrine entities. It integrates with Symfony Scheduler to run periodic entity-pruning tasks, helping keep your database tidy through simple configuration and service integration in Symfony applications.
This is a Symfony bundle, not a Laravel package — that's the first thing to reconcile before you install it. It ships a Bundle class, a DI extension, and Doctrine entity/repository wiring for pruning stale "schedule" entities. There is no service provider, no facade, no publishable config in the Laravel sense.
Minimal steps to evaluate whether it's worth your time:
DependencyInjection/*Extension.php first — that's where the cleaning service, its interval config, and the console command (if any) are registered. Everything you can actually reuse lives there.composer.json for hard requirements: the symfony/* constraints, doctrine/orm, and the tourze/* sibling packages it pulls in. Sibling tourze dependencies are the usual source of surprise transitive weight.Port the cleaner into a command, don't bridge the bundle. In Laravel the idiomatic shape is:
class PruneSchedules extends Command
{
protected $signature = 'schedule:prune {--days=30} {--dry-run} {--chunk=500}';
public function handle(): int
{
$cutoff = now()->subDays((int) $this->option('days'));
$query = Schedule::query()
->where('status', 'finished')
->where('updated_at', '<', $cutoff)
->orderBy('id');
if ($this->option('dry-run')) {
$this->info($query->count().' rows would be deleted');
return self::SUCCESS;
}
$deleted = $query->chunkById((int) $this->option('chunk'), function ($rows) {
Schedule::whereKey($rows->modelKeys())->delete();
});
return self::SUCCESS;
}
}
Mirror the bundle's config as Laravel config. Whatever the bundle exposes (retention window, statuses eligible for cleanup, batch size), put it in config/schedule-clean.php and read it with config(). This is the piece that actually carries over — it keeps the retention policy reviewable and environment-specific.
Schedule it off-peak with withoutOverlapping(). Pruning is a write-heavy, low-value-priority job:
$schedule->command('schedule:prune')->dailyAt('03:15')->withoutOverlapping()->onOneServer();
If you're genuinely running the bundle in a Symfony app alongside a Laravel service, treat it as an external system: invoke the Symfony console command via Process from a queued job, capture exit code and stderr, and let the Symfony side own the schema. Don't let two ORMs write the same table.
Soft-delete first, hard-delete later. Two-stage is much safer for a package whose behavior you don't fully control: ->where('deleted_at', '<', $cutoff)->forceDelete() in a second, separately scheduled command.
1.1.0...1.1.1 compare link with no commit summary. Treat it as a safe patch bump, pin ^1.1.0, and diff the two tags (git log 1.1.0..1.1.1 --oneline) yourself before assuming anything. Never infer "no breaking changes" from a missing changelog on a 1.x line — check the tag diff.composer require in a Laravel app will pull Symfony DI, HttpKernel and possibly Doctrine components you don't use. The service provider autodiscovery will not find anything useful. This is the single biggest pitfall; the rest follow from it.config/packages/*.yaml. There's no php artisan vendor:publish. To see the real defaults, read the extension's load() and any Configuration.php tree — the README rarely lists them all.now()->subDays(30) in a DST-transition week shifts your cutoff by an hour — irrelevant for a 30-day window, but it matters if you ever use hour-level retention. Use < on a stored timestamp rather than between-style date arithmetic.->delete() on a filtered query. A large unbounded delete will hold locks and blow up your replication lag. chunkById (not chunk) so mutated rows can't cause skips.ON DELETE behavior on dependent tables before enabling in production, and run the dry-run for at least one full retention cycle.debug:container / debug:event-dispatcher tell you whether the cleaner service is actually registered and tagged. When ported to Laravel, the equivalent failure is a where clause that matches zero rows — log the pre-delete count every run, not just the deletion count.tourze/* dependency chain. These bundles frequently depend on several siblings, each with their own release cadence. Pin them together and upgrade as a group; a single patch on one sibling is where breakage actually shows up.How can I help you explore Laravel packages today?