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 toImport
- Thanks to the foreign key in the table used for
Event
, events related to yourImport
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
Implementing Acl for My PHP Application
Have Trouble Installing PHPmyadmin on PHP7 Apache/2.4.7 (Ubuntu)
What Do Two Colons Mean in PHP
PHP Sessions to Authenticate User on Login Form
How to Validate on Max File Size in Laravel
How to Receive a File via Http Put with PHP
What Is Unexpected T_Variable in PHP
Cannot Use Concatenation When Declaring Default Class Properties in PHP
Fatal Error: Cannot Use Object of Type Stdclass as Array In
Read in Text File Line by Line PHP - Newline Not Being Detected
Add Allow_Url_Fopen to My PHP.Ini Using .Htaccess
JSON_Encode or Remove Last Comma
How to Flush Data to Browser But Continue Executing
Get String Between - Find All Occurrences PHP
Why Are Certain Types of Prepared Queries Using Pdo in PHP with MySQL Slow