wernerdweight/doctrine-cascade-soft-delete-bundle
wernerdweight/doctrine-cascade-soft-delete-bundle adds cascade soft-delete support to Doctrine entities in Symfony, helping related records be marked as deleted alongside their parent while respecting entity associations and configurable cascade behavior.
gedmo/doctrine-extensions (SoftDeleteable filter/listener) is already installed and configured on your entities. Set that up first — the bundle does nothing useful without an active soft-delete extension.composer require wernerdweight/doctrine-cascade-soft-delete-bundle. Given the last release is February 2023 and there are no dependents, verify the doctrine/orm, symfony/framework-bundle, and PHP version constraints against your project before committing — you may need --with-all-dependencies and a pinned minor.config/bundles.php:
WernerDweight\DoctrineCascadeSoftDeleteBundle\DoctrineCascadeSoftDeleteBundle::class => ['all' => true],
DependencyInjection/Configuration.php and the entity listener/service wiring first. This is a small, low-traffic package; the config schema and service definitions are the most reliable documentation of what it actually supports (config keys, tagged services, event subscriptions). Do that before trusting any README examples.Category entity with a children OneToMany, where soft-deleting the parent should also soft-delete its descendants in one flush. Annotate/attribute the association (or register the entity in the bundle config — the package exposes a marker so the listener knows which associations to walk), call $em->remove($parent) + $em->flush(), and assert deletedAt is set on children.$children) or list associations in config, not to the whole class — this keeps the propagation graph explicit and auditable.remove() → flush() cycle. The listener participates in preSoftDelete/preRemove events, so all cascaded children are scheduled as UPDATEs inside the same transaction. Don't call flush() manually between levels.softDeleteWithCascade(Entity $e) method on a base repository (or a small domain service) so controllers don't need to know the bundle exists. This also gives you one seam to swap if you later replace the package.UPDATE only if you accept bypassing the listener; otherwise loop and rely on the cascade to keep behavior consistent. Document which path is used where.deletedAt fields are non-null and that find() no longer returns them. Since the package has zero community traction, this test is your contract.@Gedmo\SoftDeleteable (or the attribute) is missing on the child class, the listener will issue a real DELETE for it via Doctrine's native cascade, silently destroying data. Audit every entity in the cascade graph.A→B→A); if it isn't guarded, you'll get an infinite loop or a stack overflow on flush.deletedAt provenance is lost. A child deleted via cascade looks identical to one deleted directly. If you need to distinguish (e.g., restore only parents), add a cascadeDeleted boolean or deletedByParent FK yourself — the bundle won't do it for you.UPDATE. A tree of 10k nodes becomes 10k UPDATEs plus hydration. Use Doctrine\ORM\Query::HINT_REFRESH/iterate if needed, or batch at the tree root level with a documented exception path.preRemove/preUpdate listeners, ordering matters. Register your listener with a lower priority than the cascade listener (check the bundle's services.yaml for the tag priority) so you see the final state, not intermediate.composer.json, and watch out for deprecations against newer Doctrine ORM (3.x attribute mapping vs. annotations) and Symfony 7 — the bundle's DI config may still reference removed classes.services.yaml decorates: or setAlias. Prefer decorating over forking.UPDATE ... SET deleted_at = ... only, never DELETE FROM. If you see DELETE, either the marker is missing or the SoftDeleteable filter is disabled for that entity.How can I help you explore Laravel packages today?