sbamtr/laravel-auto-hard-deleter
Automatically hard delete soft-deleted Eloquent models after a configurable retention period. Add a trait to your models, set how long records should linger in the trash, and let Laravel's scheduler permanently purge them, with no manual cleanup jobs required.
Architecture fit. The package addresses a real and narrow gap in Laravel's Eloquent lifecycle: SoftDeletes gives you a reversible delete, but ships no mechanism for eventual purge, so deleted_at rows accumulate indefinitely. The implementation pattern — a trait applied to a model declaring a retention window, paired with a console command that iterates expired rows and calls forceDelete() — sits cleanly inside idiomatic Laravel and requires no infrastructure. Architecturally, however, it places a data-governance policy (retention) inside the domain model. For a TPM, that framing matters: retention is typically owned by legal/compliance and expressed as configuration, not as a property hard-coded on a class. A package that cannot externalize retention to config or policy layer will be awkward to audit.
Integration feasibility. Mechanically trivial: composer require, add the trait, set the window property, register the command in the scheduler. First-pass integration is an afternoon, not a sprint. The blocker is not effort — it is version viability.
Technical risk. This is the dominant finding and it is high:
illuminate/support. If it does install via a loose constraint, that is worse — untested code paths against a framework whose soft-delete internals have moved.Key questions to resolve before any decision:
composer.json, and does it resolve against our current framework version?chunk() expired records, or load the full result set into memory?forceDelete() per model instance (firing model events and observers, N queries) or issue a mass query delete (fast, but silently bypasses observers, file/media cleanup, and downstream cascade logic)?deleted_at column names, multi-tenant scopes, or global scopes on the model?withoutOverlapping()?Stack fit. If the platform is on Laravel 8 or earlier, adoption is technically viable. On Laravel 9+, assume it does not fit without a fork and patch. Given Archived: yes and a release date over three years stale, the realistic recommendation for any actively maintained stack is: do not adopt the package; adopt the pattern.
Migration path. The functionality is roughly 100–150 lines of first-party code and is low-risk to own:
PurgeSoftDeletedRecords artisan command accepting --model, --days, --dry-run, --chunk, --limit.config/retention.php mapping each soft-deletable model to a retention window, sourced from env where compliance requires it.withoutOverlapping() and a bounded runtime.(deleted_at) per table.This replaces the dependency entirely, removes the supply-chain finding, and puts retention under configuration review — which is where auditors expect to find it.
Compatibility. If a fork is preferred over a rewrite (e.g., to preserve existing behavior), vendor the package under an internal namespace, tighten the composer constraint to the versions actually tested, and wrap the trait in an application-owned trait so the underlying implementation can be swapped without touching 20 models. Either way, verify against the current framework: the SoftDeletes trait's forceDelete() contract and deleted_at handling have been stable, but the package's assumptions about them were never tested past 2022.
Sequencing.
SoftDeletes; there are usually more than anyone expects.Maintenance. Archived means you own it from day one. Budget either a fork maintenance line item or the rewrite. The rewrite is almost certainly cheaper than carrying a vendored orphan for multiple framework upgrades.
Support. No upstream maintainer, no issue triage, no security advisories. With zero dependents there is no community to fall back on. Automated dependency and supply-chain scanners will flag this permanently; the exception request will outlive the engineer who filed it.
Scaling. This is the area where thin packages of this age most often fail. Naive implementations iterate the full expired set in PHP, which is a memory incident waiting for the first large table. Hard-deleting millions of rows in a single transaction causes table locks, write-ahead log / binlog bloat, replication lag on read replicas, and lock contention with live traffic. Mitigations must be explicit: chunked deletes (1k–5k per batch), a sleep() between batches, an index on deleted_at, off-peak scheduling, and a hard row-limit cap per run. Verify these are present before enabling — do not assume.
Failure modes to design against:
onDelete('cascade') means purging a parent silently removes children that may have their own retention obligations. Audit FKs before the first production run.forceDelete(), the inverse risk applies: an observer throwing mid-batch leaves partial state.0 or null window interpreted as "delete now" is a one-line data-loss event. Validate configuration at boot and refuse to run on ambiguous values.withoutOverlapping() compound lock pressure.deleted_at, causing premature purges.Ramp-up. Developer ramp-up is negligible — the trait-plus-command pattern is immediately legible to any Laravel engineer. The real ramp is organizational: establishing who owns the retention policy, who signs off on windows, and who is paged when a purge run behaves unexpectedly. That work is identical whether you adopt the package or build it, which further weakens the case for the dependency.
Verdict. Low score (0.1625), archived, zero dependents, and a destructive core function is a disqualifying combination. Reimplement the pattern in-house behind a config-driven, dry-run-first command. The package is a useful reference design, not a dependency.
How can I help you explore Laravel packages today?