Understanding Doctrine Cascade Operations

Understanding Doctrine Cascade Operations

persist & remove

You are correct about cascade={"persist"} meaning that persisting entity A, Doctrine will also persist all B entities in the Collection.

You are also correct about cascade={"remove"} meaning that removing entity A, Doctrine will also remove all B entities in the Collection.

But I doubt you would ever want to use this on a ManyToMany association, because when you remove entity A that cascades this operation to all B entities, those B entities might be associated to other A entities.

detach & merge

You are not correct about cascade={"detach"} and cascade={"merge"}:

Adding/removing entities from the Collection is something you need to do (in your code). Read about that here.

Detach means that you detach an entity from the EntityManager. The EntityManager will no longer manage that entity. This makes a detached entity the same as a newly instantiated entity, except that it's already in the database (but you made the EntityManager unaware of that).

In other words: cascade={"detach"} means that detaching entity A, Doctrine will also detach all B entities in the Collection.

Merge is the opposite of detach: You will merge a detached entity back into the EntityManager.

Do note that merge() will actually return a new managed object, the detached object you passed to it remains unmanaged.

Doctrine Cascade Options for OneToMany

In the Doctrine2 documentation "9.6. Transitive persistence / Cascade Operations" there are few examples of how you should configure your entities so that when you persist $article, the $topic would be also persisted. In your case I'd suggest this annotation for Topic entity:

/**
* @OneToMany(targetEntity="Article", mappedBy="topic", cascade={"persist", "remove"})
*/
private $articles;

The drawback of this solution is that you have to include $articles collection to Topic entity, but you can leave it private without getter/setter.

And as @kurt-krueckeberg mentioned, you must pass the real Topic entity when creating new Article, i.e.:

$topic = $em->getRepository('Entity\Topic')->find($id);
$article = new Article($topic);
$em->persist($article);
$em->flush();

// perhaps, in this case you don't even need to configure cascade operations

Good luck!

How do I use the cascade option in Symfony2 Doctrine?

Try using

/**
* @ORM\ManyToOne(targetEntity="Report", inversedBy="responses")
* @ORM\JoinColumn(name="reportId", referencedColumnName="id", onDelete="CASCADE")
*/
protected $report;

And then update yor schema. It will add database level Cascading

Doctrine 2 multi-level OneToOne Cascade

I think @CappY is right.

The problem is in the Status entity. when you do getBattery() and create a new Battery instance, it's related to the Status instance on which you called getBattery().

Since that instance hasn't been stored in the database yet, it's id hasn't been generated (because it's annotated as @GeneratedValue). you're almost right about cascade persist. except for that it's performed in memory.

So you need to persist and flush Status entity before doing getBattery() if you want to use that entity as id in Battery. Or else you could simple add an id field for Battery :)

Doctrine: cascade=remove vs orphanRemoval=true

The basic difference between them is:

When using the orphanRemoval=true option Doctrine makes the assumption
that the entities are privately owned and will NOT be reused by other
entities. If you neglect this assumption your entities will get
deleted by Doctrine even if you assigned the orphaned entity to
another one.

Say your User has one-to-many relation to Comment. If you are using cascade="remove", you can remove the reference for Comment from one User, and then attach that Comment to another User. When you persist them, they will be correctly saved. But if you are using orphanRemoval=true, even if you will remove given Comment from one User, and then attach to another User, this comment will be deleted during persist, because the reference has been deleted.

Doctrine cascade={remove} not working when the entity is deleted through onDelete=CASCADE

The problem you are describing is the expected behaviour. The onDelete="CASCADE" option enforces a behaviour that is executed internally by your database while the option cascade={"remove"} is handled via Doctrine and applied to objects that are performed in memory as stated in the documentation:

Cascade operations are performed in memory. That means collections and related entities are fetched into memory (even if they are marked as lazy) when the cascade operation is about to be performed. This approach allows entity lifecycle events to be performed for each of these operations.

Both approaches are valid but they imply different things as discussed in this section.


What actually happens with your setup is that you expect a mix of onDelete="CASCADE" and cascade={"remove"} to operate together in your scenario which they can't due to their nature.

As it is, since you don't have an inverse side in Import with cascade={"remove"}, when you remove an Import:

  • a DELETE operation is performed in your database on the table corresponding to Import
  • Thanks to the foreign key in the table used for Event, events related to your Import are deleted directly in your database

From there, nothing else is performed since the table used for Media does not have any foreign key referring to the Event table (since it is on the inverse side of the association).


There are two things you can do to make this work:

Invert the Media/Event association and add onDelete="CASCADE" on Media table

in Media.php

/**
* @ORM\OneToOne(targetEntity="Event", inversedBy="media")
* @ORM\JoinColumn(onDelete="CASCADE")
*/
private $event;

in Event.php

/**
* @ORM\OneToOne(targetEntity="Media", mappedBy="event")
*/
private $media;

With this approach, removing an Import entry in the database will necessarily remove related Event entries and through the same mechanism, related Media will be deleted (all this being directly done by your database, Doctrine just issued one DELETE operation on the Import table).

Add an inverse side in Import and use cascade={"remove"}

If you add an inverse OneToMany in Import with cascade={"remove"}, remove operations performed with the entity manager will be cascaded to related Event entities which will also cascade the remove operation to any associated Media.

This can be useful if you want lifecycle events to be performed for those entities.


This does not mean that you have to chose between both approaches. The documentation states the following:

You should be aware however that using strategy 1 (CASCADE=REMOVE) completely by-passes any foreign key onDelete=CASCADE option, because Doctrine will fetch and remove all associated entities explicitly nevertheless.

That being said, having onDelete=CASCADE in addition to cascade={"remove"} makes sense if you want your database to stay in a proper state. For example, if you execute DELETE queries directly (without using the entity manager), related entries would not be removed without onDelete=CASCADE and your RDBMS would most likely complain about invalid foreign key constraints.



Related Topics



Leave a reply



Submit