incompass/soft-deletable-bundle
A Symfony bundle that adds soft-delete support to Doctrine entities. Mark records as deleted without removing them from the database, automatically filter them from normal queries, and restore them when needed, helping preserve data history and build safer deletion workflows.
Architecture fit — mismatch that must be resolved before any other discussion. The package is a Symfony bundle targeting Doctrine ORM entities, not a Laravel/Eloquent package. The request framing ("Laravel/PHP package") does not match the artifact. Laravel ships Illuminate\Database\Eloquent\SoftDeletes natively, with delete(), restore(), forceDelete(), withTrashed(), and onlyTrashed() already accounted for. There is no capability gap this package fills on a standard Laravel stack. It only becomes relevant in a Laravel codebase that has adopted Doctrine via laravel-doctrine/orm — itself an uncommon architectural choice that narrows the hiring pool and adds a second ORM mental model to the team.
Integration feasibility — low. Composer will install it, but type: symfony-bundle is never auto-discovered by Laravel. Any use requires hand-registering the package's Doctrine event subscriber and query filter inside a service provider, bypassing the Symfony DI wiring the author actually tested. You would be reverse-engineering an unadopted package to use it in a way its own test suite does not cover. The maintained equivalent, Gedmo SoftDeleteable (via stof/doctrine-extensions-bundle), already solves this with documentation and a large install base.
Technical risk — high, and asymmetric. Three things drive this:
EntityManager::remove() via an event subscriber. If the subscriber is misregistered — plausible given the adapter work above — remove() degrades to a hard delete. Tests pass unless you explicitly assert the row still exists post-delete. Equally, if the SQL filter class name is wrong in config, deletes are soft but all reads return deleted rows. Both failures are quiet.Key questions a TPM must force answers to before greenlighting:
SoftDeleteable, which shares the same event model and is actively maintained?Stack fit. Only coherent in a Doctrine-backed Symfony service, or a Doctrine-backed Laravel service via laravel-doctrine/orm. For standard Eloquent Laravel: zero fit. Say this plainly in the decision record — a "Laravel package" evaluation that silently assumes a Doctrine stack will mislead downstream reviewers.
Migration path. There is no drop-in path on Laravel. Three options, in preference order:
deleted_at via migration, use SoftDeletes; on the model, use withTrashed()/onlyTrashed() explicitly. Zero dependencies, zero adapter work.SoftDeleteable with laravel-doctrine filter configuration. Config change, not code change.src/ and its tests to confirm behavior. That is engineering spend against a package with no maintainers.Compatibility. Installable at the Composer layer; the Symfony bundle metadata, auto-discovery, and bundled DI config are dead weight and must be treated as disabled. Practically, you are consuming an internal class of someone else's bundle — a contract they never intended to support. Version drift on Doctrine ORM's event system then becomes your incident.
Sequencing. (1) Confirm ORM — this is a one-hour question that eliminates most of the decision tree. (2) If Eloquent, close the evaluation and write the decision memo. (3) If Doctrine, spike Gedmo first. (4) Only if both are blocked, timebox a fork review — read the subscriber and filter, wire an integration test that asserts a soft-deleted row survives, get a named maintainer. No named owner, no adoption.
Maintenance. Assume zero upstream maintenance. A 0-star, 0-dependent package is either brand new or abandoned; both imply you fork and own it — vendoring, security patching, and tracking Doctrine breaking changes. Compare against approximately one engineering day to build the equivalent in-house, backed by the framework team on the Eloquent path or by a large community on the Gedmo path. This is the core TPM trade: a one-day saving against a multi-year ownership cost.
Support. No community, no issue trail, no docs beyond a README. Any engineer who hits an edge case starts from source. On Eloquent, the equivalent support surface is the framework docs, Stack Overflow, and Laravel's own release notes.
Scaling — the costs are inherent to soft delete, not this package. Index on deleted_at; every ORM query gains a filter; tables grow monotonically; soft-deleted rows still collide with unique constraints, so "delete then re-create with the same SKU/email" fails at runtime rather than at migration time. Most dangerously, the filter lives in the Doctrine layer: raw SQL, DB::raw, reporting extracts, and bulk jobs will return deleted rows. This is true of Eloquent too, but it is well-understood there and withTrashed() makes intent legible in review.
Failure modes to enumerate in the risk register:
Ramp-up. High, and for the wrong reason: engineers must learn Doctrine entity/metadata modeling, the Doctrine event system, and Symfony bundle conventions to obtain a feature the framework already provides. Eloquent ramp-up is one line in a model.
Recommendation. Reject. On Laravel, implement soft delete with the native SoftDeletes trait — no dependency, no adapter, no fork. If Doctrine is a hard architectural constraint, adopt Gedmo SoftDeleteable. Reach for incompass/soft-deletable-bundle only if both are genuinely blocked and a named owner accepts the fork. Separately, flag to product that soft delete is not GDPR/CCPA erasure — deleted rows persist — so a hard-delete and retention/purge policy must be scoped as its own backlog item regardless of which implementation wins.
How can I help you explore Laravel packages today?