Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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/1
HTTP/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/1

NOW 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.