You are browsing a version that has not yet been released. |
Bi-Directional References
By default when you map a bi-directional reference, the reference is maintained on both sides of the relationship and there is not a single "owning side". Both sides are considered owning and changes are tracked and persisted separately. Here is an example:
1 <?php
#[Document]
class BlogPost
{
// ...
#[ReferenceOne(targetDocument: User::class)]
private User $user;
}
#[Document]
class User
{
// ...
/** @var Collection<BlogPost> */
#[ReferenceMany(targetDocument: BlogPost::class)]
private Collection $posts;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
When I persist some instances of the above classes the references would exist on both sides! The
BlogPost
collection would have a DBRef stored on the $user
property and the User
collection would have a DBRef stored in the $posts
property.
Owning and Inverse Sides
A user may have lots of posts and we don't need to store a reference to each post on the user, we can get the users post by running a query like the following:
1 db.BlogPost.find({ 'user.$id' : user.id })
In order to map this you can use the inversedBy
and mappedBy
options. Here is the same
example above where we implement this:
One to Many
1 <?php
#[Document]
class BlogPost
{
// ...
#[ReferenceOne(targetDocument: User::class, inversedBy: 'posts')]
private User $user;
}
#[Document]
class User
{
// ...
/** @var Collection<BlogPost> */
#[ReferenceMany(targetDocument: BlogPost::class, mappedBy: 'user')]
private Collection $posts;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
So now when we persist a User
and multiple BlogPost
instances for that User
:
And we retrieve the User
later to access the posts for that user:
The above will execute a query like the following to lazily load the collection of posts to iterate over:
1 db.BlogPost.find( { 'user.$id' : user.id } )
Remember that the inverse side, the side which specified |
Other Examples
Here are several examples which implement the inversedBy
and mappedBy
options:
One to One
Here is an example where we have a one to one relationship between Cart
and Customer
:
1 <?php
#[Document]
class Cart
{
// ...
#[ReferenceOne(targetDocument: Customer::class, inversedBy: 'cart')]
public Customer $customer;
}
#[Document]
class Customer
{
// ...
#[ReferenceOne(targetDocument: Cart::class, mappedBy: 'customer')]
public Cart $cart;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The owning side is on Cart.customer
and the Customer.cart
referenced is loaded with a query
like this:
1 db.Cart.find( { 'customer.$id' : customer.id } )
If you want to nullify the relationship between a Cart
instance and Customer
instance
you must null it out on the Cart.customer
side:
When specifying inverse one-to-one relationships the referenced document is
loaded directly when the owning document is hydrated instead of using a
proxy. In the example above, loading a |
Self-Referencing Many to Many
1 <?php
namespace Documents;
#[Document]
class User
{
// ...
/** @var Collection<User> */
#[ReferenceMany(targetDocument: User::class, mappedBy: 'myFriends')]
public Collection $friendsWithMe;
/** @var Collection<User> */
#[ReferenceMany(targetDocument: User::class, inversedBy: 'friendsWithMe')]
public Collection $myFriends;
public function __construct($name)
{
$this->name = $name;
$this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection();
$this->myFriends = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addFriend(User $user): void
{
$user->friendsWithMe[] = $this;
$this->myFriends[] = $user;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30