Cascading Deletes and orphanRemoval
Cascading Deletes and orphanRemoval
~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
What happens when a project WITH existing tasks gets deleted? WITHOUT further configuration, the database REJECTS it due to the foreign key relationship.
Provoking the error
curl -k -i -X DELETE https://localhost/api/projects/1HTTP/2 500
{
"hydra:description": "An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation"
}Achtung: Status 500, NOT a clean 422 – a database error is NOT a validation error in the sense of chapter 19, but an UNHANDLED technical error. This MUST be fixed, a 500 must NEVER be the EXPECTED state of an API.
cascade: ['remove'] on OneToMany
#[ORM\OneToMany(
targetEntity: Task::class,
mappedBy: 'project',
cascade: ['remove'],
orphanRemoval: true,
)]
#[Groups(['project:read'])]
private Collection $tasks;cascade: ['remove'] AUTOMATICALLY deletes ALL related tasks TOO, as soon as the project gets deleted – EXACTLY the same concept as in the Symfony course (chapter 33).
orphanRemoval explained
orphanRemoval: true handles an ADDITIONAL, more SUBTLE case: when a task gets REMOVED from $project->getTasks() (WITHOUT assigning it to another project), Doctrine AUTOMATICALLY deletes it, instead of leaving it as an "orphan" with project = NULL in the database – which would be IMPOSSIBLE anyway, since project is nullable: false (chapter 37).
Testing the new behavior
curl -k -i -X DELETE https://localhost/api/projects/1NOW 204 No Content – the project AND all its tasks are gone, WITHOUT an integrity error.
Achtung: cascade: ['remove'] is FINAL and IRREVERSIBLE – for a PRODUCTION system, it's worth weighing whether a "soft delete" (a deletedAt field instead of a real delete) isn't the SAFER choice, especially for data with business value.
Tipp: For the ManyToMany relationship to Tag (chapter 42), NO cascade: ['remove'] is needed – when a task gets deleted, Doctrine AUTOMATICALLY removes ONLY the join table entries, the actual Tag records remain UNTOUCHED.