You are browsing a version that is no longer maintained. |
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:
<?php#[Document]class BlogPost{ // ... #[ReferenceOne(targetDocument: User::class)] private User $user;}#[Document]class User{ // ... /** @var Collection<int, BlogPost> */ #[ReferenceMany(targetDocument: BlogPost::class)] private Collection $posts;}
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:
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
<?php#[Document]class BlogPost{ // ... #[ReferenceOne(targetDocument: User::class, inversedBy: 'posts')] private User $user;}#[Document]class User{ // ... /** @var Collection<int, BlogPost> */ #[ReferenceMany(targetDocument: BlogPost::class, mappedBy: 'user')] private Collection $posts;}
So now when we persist a User and multiple BlogPost instances for that User:
<?php$user = new User();$post1 = new BlogPost();$post1->setUser($user);$post2 = new BlogPost();$post2->setUser($user);$post3 = new BlogPost();$post3->setUser($user);$dm->persist($post1);$dm->persist($post2);$dm->persist($post3);$dm->flush();
And we retrieve the User later to access the posts for that user:
<?php$user = $dm->find(User::class, $user->id);$posts = $user->getPosts();foreach ($posts as $post) { // ...}
The above will execute a query like the following to lazily load the collection of posts to iterate over:
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:
<?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;}
The owning side is on Cart.customer and the Customer.cart referenced is loaded with a query
like this:
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:
<?php$cart->setCustomer(null);$dm->flush();
|
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
<?phpnamespace Documents;#[Document]class User{ // ... /** @var Collection<int, User> */ #[ReferenceMany(targetDocument: User::class, mappedBy: 'myFriends')] public Collection $friendsWithMe; /** @var Collection<int, 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; }}
