@@ -37,51 +37,51 @@ information about its type and if it's the owning or inverse side.
37
37
{
38
38
/** @Id @GeneratedValue @Column(type="string") */
39
39
private $id;
40
-
40
+
41
41
/**
42
42
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
43
43
*
44
44
* @ManyToMany(targetEntity="Comment", inversedBy="userFavorites")
45
45
* @JoinTable(name="user_favorite_comments")
46
46
*/
47
47
private $favorites;
48
-
48
+
49
49
/**
50
50
* Unidirectional - Many users have marked many comments as read
51
51
*
52
52
* @ManyToMany(targetEntity="Comment")
53
53
* @JoinTable(name="user_read_comments")
54
54
*/
55
55
private $commentsRead;
56
-
56
+
57
57
/**
58
58
* Bidirectional - One-To-Many (INVERSE SIDE)
59
59
*
60
60
* @OneToMany(targetEntity="Comment", mappedBy="author")
61
61
*/
62
62
private $commentsAuthored;
63
-
63
+
64
64
/**
65
65
* Unidirectional - Many-To-One
66
66
*
67
67
* @ManyToOne(targetEntity="Comment")
68
68
*/
69
69
private $firstComment;
70
70
}
71
-
71
+
72
72
/** @Entity */
73
73
class Comment
74
74
{
75
75
/** @Id @GeneratedValue @Column(type="string") */
76
76
private $id;
77
-
77
+
78
78
/**
79
79
* Bidirectional - Many comments are favorited by many users (INVERSE SIDE)
80
80
*
81
81
* @ManyToMany(targetEntity="User", mappedBy="favorites")
82
82
*/
83
83
private $userFavorites;
84
-
84
+
85
85
/**
86
86
* Bidirectional - Many Comments are authored by one user (OWNING SIDE)
87
87
*
@@ -100,19 +100,19 @@ definitions omitted):
100
100
firstComment_id VARCHAR(255) DEFAULT NULL,
101
101
PRIMARY KEY(id)
102
102
) ENGINE = InnoDB;
103
-
103
+
104
104
CREATE TABLE Comment (
105
105
id VARCHAR(255) NOT NULL,
106
106
author_id VARCHAR(255) DEFAULT NULL,
107
107
PRIMARY KEY(id)
108
108
) ENGINE = InnoDB;
109
-
109
+
110
110
CREATE TABLE user_favorite_comments (
111
111
user_id VARCHAR(255) NOT NULL,
112
112
favorite_comment_id VARCHAR(255) NOT NULL,
113
113
PRIMARY KEY(user_id, favorite_comment_id)
114
114
) ENGINE = InnoDB;
115
-
115
+
116
116
CREATE TABLE user_read_comments (
117
117
user_id VARCHAR(255) NOT NULL,
118
118
comment_id VARCHAR(255) NOT NULL,
@@ -135,7 +135,7 @@ relations of the ``User``:
135
135
public function getReadComments() {
136
136
return $this->commentsRead;
137
137
}
138
-
138
+
139
139
public function setFirstComment(Comment $c) {
140
140
$this->firstComment = $c;
141
141
}
@@ -148,17 +148,17 @@ The interaction code would then look like in the following snippet
148
148
149
149
<?php
150
150
$user = $em->find('User', $userId);
151
-
151
+
152
152
// unidirectional many to many
153
153
$comment = $em->find('Comment', $readCommentId);
154
154
$user->getReadComments()->add($comment);
155
-
155
+
156
156
$em->flush();
157
-
157
+
158
158
// unidirectional many to one
159
159
$myFirstComment = new Comment();
160
160
$user->setFirstComment($myFirstComment);
161
-
161
+
162
162
$em->persist($myFirstComment);
163
163
$em->flush();
164
164
@@ -171,40 +171,40 @@ fields on both sides:
171
171
class User
172
172
{
173
173
// ..
174
-
174
+
175
175
public function getAuthoredComments() {
176
176
return $this->commentsAuthored;
177
177
}
178
-
178
+
179
179
public function getFavoriteComments() {
180
180
return $this->favorites;
181
181
}
182
182
}
183
-
183
+
184
184
class Comment
185
185
{
186
186
// ...
187
-
187
+
188
188
public function getUserFavorites() {
189
189
return $this->userFavorites;
190
190
}
191
-
191
+
192
192
public function setAuthor(User $author = null) {
193
193
$this->author = $author;
194
194
}
195
195
}
196
-
196
+
197
197
// Many-to-Many
198
198
$user->getFavorites()->add($favoriteComment);
199
199
$favoriteComment->getUserFavorites()->add($user);
200
-
200
+
201
201
$em->flush();
202
-
202
+
203
203
// Many-To-One / One-To-Many Bidirectional
204
204
$newComment = new Comment();
205
205
$user->getAuthoredComments()->add($newComment);
206
206
$newComment->setAuthor($user);
207
-
207
+
208
208
$em->persist($newComment);
209
209
$em->flush();
210
210
@@ -225,10 +225,10 @@ element. Here are some examples:
225
225
// Remove by Elements
226
226
$user->getComments()->removeElement($comment);
227
227
$comment->setAuthor(null);
228
-
228
+
229
229
$user->getFavorites()->removeElement($comment);
230
230
$comment->getUserFavorites()->removeElement($user);
231
-
231
+
232
232
// Remove by Key
233
233
$user->getComments()->remove($ithComment);
234
234
$comment->setAuthor(null);
@@ -240,7 +240,7 @@ Notice how both sides of the bidirectional association are always
240
240
updated. Unidirectional associations are consequently simpler to
241
241
handle.
242
242
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
244
244
have to specify a nullable type, i.e. ``setAddress(?Address $address) ``,
245
245
otherwise ``setAddress(null) `` will fail to remove the association.
246
246
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.
271
271
272
272
Say you clear a collection of tags by calling
273
273
``$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
276
276
calls.
277
277
278
278
Association Management Methods
@@ -296,38 +296,38 @@ example that encapsulate much of the association management code:
296
296
// Collections implement ArrayAccess
297
297
$this->commentsRead[] = $comment;
298
298
}
299
-
299
+
300
300
public function addComment(Comment $comment) {
301
301
if (count($this->commentsAuthored) == 0) {
302
302
$this->setFirstComment($comment);
303
303
}
304
304
$this->comments[] = $comment;
305
305
$comment->setAuthor($this);
306
306
}
307
-
307
+
308
308
private function setFirstComment(Comment $c) {
309
309
$this->firstComment = $c;
310
310
}
311
-
311
+
312
312
public function addFavorite(Comment $comment) {
313
313
$this->favorites->add($comment);
314
314
$comment->addUserFavorite($this);
315
315
}
316
-
316
+
317
317
public function removeFavorite(Comment $comment) {
318
318
$this->favorites->removeElement($comment);
319
319
$comment->removeUserFavorite($this);
320
320
}
321
321
}
322
-
322
+
323
323
class Comment
324
324
{
325
325
// ..
326
-
326
+
327
327
public function addUserFavorite(User $user) {
328
328
$this->userFavorites[] = $user;
329
329
}
330
-
330
+
331
331
public function removeUserFavorite(User $user) {
332
332
$this->userFavorites->removeElement($user);
333
333
}
@@ -373,7 +373,7 @@ as your preferences.
373
373
Synchronizing Bidirectional Collections
374
374
---------------------------------------
375
375
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
377
377
responsibility of keeping the collections on the owning and inverse side
378
378
in sync when you apply changes to them. Doctrine can only
379
379
guarantee a consistent state for the hydration, not for your client
@@ -387,7 +387,7 @@ can show the possible caveats you can encounter:
387
387
<?php
388
388
$user->getFavorites()->add($favoriteComment);
389
389
// not calling $favoriteComment->getUserFavorites()->add($user);
390
-
390
+
391
391
$user->getFavorites()->contains($favoriteComment); // TRUE
392
392
$favoriteComment->getUserFavorites()->contains($user); // FALSE
393
393
@@ -422,7 +422,7 @@ comment might look like in your controller (without ``cascade: persist``):
422
422
$user = new User();
423
423
$myFirstComment = new Comment();
424
424
$user->addComment($myFirstComment);
425
-
425
+
426
426
$em->persist($user);
427
427
$em->persist($myFirstComment); // required, if `cascade: persist` is not set
428
428
$em->flush();
@@ -480,7 +480,7 @@ If you then set up the cascading to the ``User#commentsAuthored`` property...
480
480
<?php
481
481
$user = new User();
482
482
$user->comment('Lorem ipsum', new DateTime());
483
-
483
+
484
484
$em->persist($user);
485
485
$em->flush();
486
486
@@ -559,6 +559,13 @@ OrphanRemoval works with one-to-one, one-to-many and many-to-many associations.
559
559
If you neglect this assumption your entities will get deleted by Doctrine even if
560
560
you assigned the orphaned entity to another one.
561
561
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
+
562
569
As a better example consider an Addressbook application where you have Contacts, Addresses
563
570
and StandingData:
564
571
@@ -578,10 +585,10 @@ and StandingData:
578
585
/** @Id @Column(type="integer") @GeneratedValue */
579
586
private $id;
580
587
581
- /** @OneToOne(targetEntity="StandingData", orphanRemoval=true) */
588
+ /** @OneToOne(targetEntity="StandingData", cascade={"persist"}, orphanRemoval=true) */
582
589
private $standingData;
583
590
584
- /** @OneToMany(targetEntity="Address", mappedBy="contact", orphanRemoval=true) */
591
+ /** @OneToMany(targetEntity="Address", mappedBy="contact", cascade={"persist"}, orphanRemoval=true) */
585
592
private $addresses;
586
593
587
594
public function __construct()
@@ -612,10 +619,10 @@ Now two examples of what happens when you remove the references:
612
619
613
620
$em->flush();
614
621
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
619
626
from the database.
620
627
621
628
.. _filtering-collections :
0 commit comments