Skip to content

Commit 3d9d168

Browse files
committed
Improve orphanRemoval documentation
1 parent c0a1404 commit 3d9d168

File tree

1 file changed

+53
-46
lines changed

1 file changed

+53
-46
lines changed

docs/en/reference/working-with-associations.rst

+53-46
Original file line numberDiff line numberDiff line change
@@ -37,51 +37,51 @@ information about its type and if it's the owning or inverse side.
3737
{
3838
/** @Id @GeneratedValue @Column(type="string") */
3939
private $id;
40-
40+
4141
/**
4242
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
4343
*
4444
* @ManyToMany(targetEntity="Comment", inversedBy="userFavorites")
4545
* @JoinTable(name="user_favorite_comments")
4646
*/
4747
private $favorites;
48-
48+
4949
/**
5050
* Unidirectional - Many users have marked many comments as read
5151
*
5252
* @ManyToMany(targetEntity="Comment")
5353
* @JoinTable(name="user_read_comments")
5454
*/
5555
private $commentsRead;
56-
56+
5757
/**
5858
* Bidirectional - One-To-Many (INVERSE SIDE)
5959
*
6060
* @OneToMany(targetEntity="Comment", mappedBy="author")
6161
*/
6262
private $commentsAuthored;
63-
63+
6464
/**
6565
* Unidirectional - Many-To-One
6666
*
6767
* @ManyToOne(targetEntity="Comment")
6868
*/
6969
private $firstComment;
7070
}
71-
71+
7272
/** @Entity */
7373
class Comment
7474
{
7575
/** @Id @GeneratedValue @Column(type="string") */
7676
private $id;
77-
77+
7878
/**
7979
* Bidirectional - Many comments are favorited by many users (INVERSE SIDE)
8080
*
8181
* @ManyToMany(targetEntity="User", mappedBy="favorites")
8282
*/
8383
private $userFavorites;
84-
84+
8585
/**
8686
* Bidirectional - Many Comments are authored by one user (OWNING SIDE)
8787
*
@@ -100,19 +100,19 @@ definitions omitted):
100100
firstComment_id VARCHAR(255) DEFAULT NULL,
101101
PRIMARY KEY(id)
102102
) ENGINE = InnoDB;
103-
103+
104104
CREATE TABLE Comment (
105105
id VARCHAR(255) NOT NULL,
106106
author_id VARCHAR(255) DEFAULT NULL,
107107
PRIMARY KEY(id)
108108
) ENGINE = InnoDB;
109-
109+
110110
CREATE TABLE user_favorite_comments (
111111
user_id VARCHAR(255) NOT NULL,
112112
favorite_comment_id VARCHAR(255) NOT NULL,
113113
PRIMARY KEY(user_id, favorite_comment_id)
114114
) ENGINE = InnoDB;
115-
115+
116116
CREATE TABLE user_read_comments (
117117
user_id VARCHAR(255) NOT NULL,
118118
comment_id VARCHAR(255) NOT NULL,
@@ -135,7 +135,7 @@ relations of the ``User``:
135135
public function getReadComments() {
136136
return $this->commentsRead;
137137
}
138-
138+
139139
public function setFirstComment(Comment $c) {
140140
$this->firstComment = $c;
141141
}
@@ -148,17 +148,17 @@ The interaction code would then look like in the following snippet
148148
149149
<?php
150150
$user = $em->find('User', $userId);
151-
151+
152152
// unidirectional many to many
153153
$comment = $em->find('Comment', $readCommentId);
154154
$user->getReadComments()->add($comment);
155-
155+
156156
$em->flush();
157-
157+
158158
// unidirectional many to one
159159
$myFirstComment = new Comment();
160160
$user->setFirstComment($myFirstComment);
161-
161+
162162
$em->persist($myFirstComment);
163163
$em->flush();
164164
@@ -171,40 +171,40 @@ fields on both sides:
171171
class User
172172
{
173173
// ..
174-
174+
175175
public function getAuthoredComments() {
176176
return $this->commentsAuthored;
177177
}
178-
178+
179179
public function getFavoriteComments() {
180180
return $this->favorites;
181181
}
182182
}
183-
183+
184184
class Comment
185185
{
186186
// ...
187-
187+
188188
public function getUserFavorites() {
189189
return $this->userFavorites;
190190
}
191-
191+
192192
public function setAuthor(User $author = null) {
193193
$this->author = $author;
194194
}
195195
}
196-
196+
197197
// Many-to-Many
198198
$user->getFavorites()->add($favoriteComment);
199199
$favoriteComment->getUserFavorites()->add($user);
200-
200+
201201
$em->flush();
202-
202+
203203
// Many-To-One / One-To-Many Bidirectional
204204
$newComment = new Comment();
205205
$user->getAuthoredComments()->add($newComment);
206206
$newComment->setAuthor($user);
207-
207+
208208
$em->persist($newComment);
209209
$em->flush();
210210
@@ -225,10 +225,10 @@ element. Here are some examples:
225225
// Remove by Elements
226226
$user->getComments()->removeElement($comment);
227227
$comment->setAuthor(null);
228-
228+
229229
$user->getFavorites()->removeElement($comment);
230230
$comment->getUserFavorites()->removeElement($user);
231-
231+
232232
// Remove by Key
233233
$user->getComments()->remove($ithComment);
234234
$comment->setAuthor(null);
@@ -240,7 +240,7 @@ Notice how both sides of the bidirectional association are always
240240
updated. Unidirectional associations are consequently simpler to
241241
handle.
242242

243-
Also note that if you use type-hinting in your methods, you will
243+
Also note that if you use type-hinting in your methods, you will
244244
have to specify a nullable type, i.e. ``setAddress(?Address $address)``,
245245
otherwise ``setAddress(null)`` will fail to remove the association.
246246
Another way to deal with this is to provide a special method, like
@@ -271,8 +271,8 @@ entities that have been re-added to the collection.
271271

272272
Say you clear a collection of tags by calling
273273
``$post->getTags()->clear();`` and then call
274-
``$post->getTags()->add($tag)``. This will not recognize the tag having
275-
already been added previously and will consequently issue two separate database
274+
``$post->getTags()->add($tag)``. This will not recognize the tag having
275+
already been added previously and will consequently issue two separate database
276276
calls.
277277

278278
Association Management Methods
@@ -296,38 +296,38 @@ example that encapsulate much of the association management code:
296296
// Collections implement ArrayAccess
297297
$this->commentsRead[] = $comment;
298298
}
299-
299+
300300
public function addComment(Comment $comment) {
301301
if (count($this->commentsAuthored) == 0) {
302302
$this->setFirstComment($comment);
303303
}
304304
$this->comments[] = $comment;
305305
$comment->setAuthor($this);
306306
}
307-
307+
308308
private function setFirstComment(Comment $c) {
309309
$this->firstComment = $c;
310310
}
311-
311+
312312
public function addFavorite(Comment $comment) {
313313
$this->favorites->add($comment);
314314
$comment->addUserFavorite($this);
315315
}
316-
316+
317317
public function removeFavorite(Comment $comment) {
318318
$this->favorites->removeElement($comment);
319319
$comment->removeUserFavorite($this);
320320
}
321321
}
322-
322+
323323
class Comment
324324
{
325325
// ..
326-
326+
327327
public function addUserFavorite(User $user) {
328328
$this->userFavorites[] = $user;
329329
}
330-
330+
331331
public function removeUserFavorite(User $user) {
332332
$this->userFavorites->removeElement($user);
333333
}
@@ -373,7 +373,7 @@ as your preferences.
373373
Synchronizing Bidirectional Collections
374374
---------------------------------------
375375

376-
In the case of Many-To-Many associations you as the developer have the
376+
In the case of Many-To-Many associations you as the developer have the
377377
responsibility of keeping the collections on the owning and inverse side
378378
in sync when you apply changes to them. Doctrine can only
379379
guarantee a consistent state for the hydration, not for your client
@@ -387,7 +387,7 @@ can show the possible caveats you can encounter:
387387
<?php
388388
$user->getFavorites()->add($favoriteComment);
389389
// not calling $favoriteComment->getUserFavorites()->add($user);
390-
390+
391391
$user->getFavorites()->contains($favoriteComment); // TRUE
392392
$favoriteComment->getUserFavorites()->contains($user); // FALSE
393393
@@ -422,7 +422,7 @@ comment might look like in your controller (without ``cascade: persist``):
422422
$user = new User();
423423
$myFirstComment = new Comment();
424424
$user->addComment($myFirstComment);
425-
425+
426426
$em->persist($user);
427427
$em->persist($myFirstComment); // required, if `cascade: persist` is not set
428428
$em->flush();
@@ -480,7 +480,7 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
480480
<?php
481481
$user = new User();
482482
$user->comment('Lorem ipsum', new DateTime());
483-
483+
484484
$em->persist($user);
485485
$em->flush();
486486
@@ -559,6 +559,13 @@ OrphanRemoval works with one-to-one, one-to-many and many-to-many associations.
559559
If you neglect this assumption your entities will get deleted by Doctrine even if
560560
you assigned the orphaned entity to another one.
561561

562+
.. note::
563+
564+
``orphanRemoval=true`` option should be used in combination with `cascade=["persist"]` option
565+
as the child entity that is manually persisted will not be deleted automatically by Doctrine
566+
when collection is still instance of ArrayCollection (before first flush / hydration).
567+
This is a Doctrine limitation since ArrayCollection does not have access to UnitOfWork.
568+
562569
As a better example consider an Addressbook application where you have Contacts, Addresses
563570
and StandingData:
564571

@@ -578,10 +585,10 @@ and StandingData:
578585
/** @Id @Column(type="integer") @GeneratedValue */
579586
private $id;
580587
581-
/** @OneToOne(targetEntity="StandingData", orphanRemoval=true) */
588+
/** @OneToOne(targetEntity="StandingData", cascade={"persist"}, orphanRemoval=true) */
582589
private $standingData;
583590
584-
/** @OneToMany(targetEntity="Address", mappedBy="contact", orphanRemoval=true) */
591+
/** @OneToMany(targetEntity="Address", mappedBy="contact", cascade={"persist"}, orphanRemoval=true) */
585592
private $addresses;
586593
587594
public function __construct()
@@ -612,10 +619,10 @@ Now two examples of what happens when you remove the references:
612619
613620
$em->flush();
614621
615-
In this case you have not only changed the ``Contact`` entity itself but
616-
you have also removed the references for standing data and as well as one
617-
address reference. When flush is called not only are the references removed
618-
but both the old standing data and the one address entity are also deleted
622+
In this case you have not only changed the ``Contact`` entity itself but
623+
you have also removed the references for standing data and as well as one
624+
address reference. When flush is called not only are the references removed
625+
but both the old standing data and the one address entity are also deleted
619626
from the database.
620627

621628
.. _filtering-collections:

0 commit comments

Comments
 (0)