You are browsing a version that has not yet been released. |
Blending the ORM and MongoDB ODM
This article demonstrates how you can integrate the Doctrine MongoDB ODM and the ORM transparently, maintaining a clean domain model. This is something that is supported indirectly by the libraries by using the events.
This example will have a Product
that is stored in MongoDB and the Order
stored in a SQL database like MySQL, PostgreSQL or SQLite.
Define Entity
Next create the Order
entity that has a $product
and $productId
property linking it to the Product
that is stored with MongoDB:
1 <?php
namespace Entities;
use Documents\Product;
#[Entity]
#[Table(name: 'orders')]
class Order
{
#[Id]
#[Column(type: 'int')]
#[GeneratedValue(strategy: 'AUTO')]
public int $id;
#[Column(type: 'string')]
private string $productId;
private Product $product;
public function getId(): ?int
{
return $this->id;
}
public function getProductId(): ?string
{
return $this->productId;
}
public function setProduct(Product $product): void
{
$this->productId = $product->getId();
$this->product = $product;
}
public function getProduct(): ?Product
{
return $this->product;
}
}
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
31
32
33
34
35
36
37
38
39
40
41
Event Subscriber
Now we need to setup an event subscriber that will set the $product
property
of all Order
instances to a reference to the document product so it can be
lazily loaded when it is accessed the first time. So first register a new event
subscriber:
or in YAML configuration of the Symfony container:
So now we need to define a class named MyEventSubscriber
and pass
DocumentManager
as a dependency. It will have a postLoad()
method that
sets the product document reference:
1 <?php
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\Event\LifecycleEventArgs;
class MyEventSubscriber
{
public function __construct(
private readonly DocumentManager $dm,
) {}
public function postLoad(LifecycleEventArgs $eventArgs): void
{
$order = $eventArgs->getEntity();
if (!$order instanceof Order) {
return;
}
$product = $this->dm->getReference(Product::class, $order->getProductId());
$eventArgs->getObjectManager()
->getClassMetadata(Order::class)
->reflClass
->getProperty('product')
->setValue($order, $product);
}
}
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
The postLoad
method will be invoked after an ORM entity is loaded from the database. This allows us
to use the DocumentManager
to set the $product
property with a reference to the Product
document
with the product id we previously stored. Please note, that the event subscriber will be called on
postLoad for all entities that are loaded by doctrine. Thus, it is recommended to check for the current
entity.
Working with Products and Orders
First create a new Product
:
Now create a new Order
and link it to a Product
in MySQL:
Later we can retrieve the entity and lazily load the reference to the document in MongoDB:
If you were to print the $order
you would see that we got back regular PHP
objects:
The above would output the following:
1 Order Object
(
[id:Entities\Order:private] => 53
[productId:Entities\Order:private] => 4c74a1868ead0ed7a9000000
[product:Entities\Order:private] => Proxies\DocumentsProductProxy Object
(
[__isInitialized__] => 1
[id:Documents\Product:private] => 4c74a1868ead0ed7a9000000
[title:Documents\Product:private] => Test Product
)
)
2
3
4
5
6
7
8
9
10
11