Skip to content

Instantly share code, notes, and snippets.

@webdevilopers
Last active August 17, 2021 01:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save webdevilopers/e65f795749c40601276f60a5a3517a85 to your computer and use it in GitHub Desktop.
Save webdevilopers/e65f795749c40601276f60a5a3517a85 to your computer and use it in GitHub Desktop.
Using event subscriber to get MongoDB ODM Documents related to ORM Entity in Doctrine
<?php
/**
* @ORM\Entity()
* @ORM\EntityListeners({"Acme\AppBundle\Event\CustomerListener"})
*/
class Customer()
{
}
<?php
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\AppBundle\Entity\Customer;
use Symfony\Component\DependencyInjection\Container;
class CustomerListener
{
/**
* @var DocumentManager
*/
private $dm;
/*
public function __construct(DocumentManager $dm)
{
$this->dm = $dm;
}
*/
public function __construct(Container $container)
{
$this->dm = $container->get('doctrine_mongodb.odm.default_document_manager');
}
public function postLoad(Customer $customer, LifecycleEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$ordersReflProp = $em->getClassMetadata('AcmeAppBundle:Customer')
->reflClass->getProperty('orders');
$ordersReflProp->setAccessible(true);
$ordersReflProp->setValue(
$customer, $this->dm->getRepository('AcmeAppBundle:Order')
->findBy(array('customer' => $customer->getId()))
);
}
}
<?php
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ORM\Event\LifecycleEventArgs;
class MyEventSubscriber
{
public function __construct(DocumentManager $dm)
{
$this->dm = $dm;
}
public function postLoad(LifecycleEventArgs $eventArgs)
{
if ($eventArgs->getObject() instanceof Customer) {
$customer = $eventArgs->getEntity();
$em = $eventArgs->getEntityManager();
$ordersReflProp = $em->getClassMetadata('AcmeBundle:Customer')
->reflClass->getProperty('orders');
$ordersReflProp->setAccessible(true);
$ordersReflProp->setValue(
$customer, $this->dm->getRepository('AcmeBundle:Order')
->findBy(array('customer' => $customer->getId()))
);
}
}
}
<?php
use Doctrine\ORM\EntityManager;
use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\Container;
class MongoEventSubscriber
{
/**
* @var EntityManager
*/
private $_em;
/*
public function __construct(EntityManager $em)
{
$this->_em = $em;
}
*/
public function __construct(Container $container)
{
$this->_em = $container->get('doctrine.orm.default_entity_manager');
}
public function postLoad(LifecycleEventArgs $eventArgs)
{
if ($eventArgs->getObject() instanceof Order) {
$order = $eventArgs->getDocument();
$dm = $eventArgs->getDocumentManager();
$supplierReflProp = $dm->getClassMetadata('AcmeBundle:Order')
->reflClass->getProperty('supplier');
$supplierReflProp->setAccessible(true);
$supplierReflProp->setValue(
$order, $this->_em->getRepository('AcmeBundle:Supplier')
->find($order->supplier())
);
}
}
}
<?php
class OrderController extends Controller
{
public function listCustomerOrdersAction()
{
$em = $this->getDoctrine()->getManager();
$dm = $this->get('doctrine_mongodb.odm.default_document_manager');
$eventManager = $em->getEventManager();
$eventManager->addEventListener(
array(\Doctrine\ORM\Events::postLoad), new DoctrineEventSubscriber($dm)
);
$eventManager2 = $dm->getEventManager();
$eventManager2->addEventListener(
array(\Doctrine\ODM\MongoDB\Events::postLoad), new MongoEventSubscriber($em)
);
$customer = $em->find('AcmeBundle:Customer', 969);
dump($customer);
$orders = $customer->orders();
dump($orders[0]);
$supplier = $orders[0]->supplier();
dump($supplier);
echo $supplier->fullName();
exit;
}
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<!--
<service id="orm.doctrine.listener" class="Acme\AppBundle\Event\DoctrineEventSubscriber">
<argument type="service" id="doctrine.odm.mongodb.document_manager"/>
<tag name="doctrine.event_listener" event="postLoad"/>
</service>
<service id="odm.doctrine.listener" class="Acme\AppBundle\Event\MongoEventSubscriber">
<argument type="service" id="doctrine.orm.entity_manager"/>
<tag name="doctrine_mongodb.odm.event_listener" event="postLoad"/>
</service>
-->
<service id="customer.listener" class="Acme\AppBundle\Event\BranchListener">
<argument type="service" id="service_container" />
<tag name="doctrine.orm.entity_listener" />
</service>
<service id="odm.doctrine.listener" class="Acme\AppBundle\Event\MongoEventSubscriber">
<argument type="service" id="service_container"/>
<tag name="doctrine_mongodb.odm.event_listener" event="postLoad"/>
</service>
</services>
</container>
@webdevilopers
Copy link
Author

@webdevilopers
Copy link
Author

webdevilopers commented Apr 26, 2016

The Event Listeners inside the Controller work fine. I would like to move them to the services.xml.
Unfortunately I get:

Circular reference detected for service "doctrine.orm.default_entity_manager", path: "cache_warmer -> twig -> security.authorization_checker -> security.authentication.manager -> fos_user.user_manager -> fos_user.entity_manager -> doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> orm.doctrine.listener -> doctrine_mongodb.odm.default_document_manager -> doctrine_mongodb.odm.default_connection -> doctrine_mongodb.odm.event_manager -> gedmo.listener.reference.mongodb".

Is my DI wrong?

Or is it a @symfony w/ @doctrine problem and I will have to use setterInjection or inject the Container?

Discussion on Twitter:
https://twitter.com/webdevilopers/status/724870430557896704

@webdevilopers
Copy link
Author

@cbeyer @stephpy Did you guys find any solution similar to issue stephpy/timeline-bundle#24 (comment)?

@webdevilopers
Copy link
Author

webdevilopers commented Apr 26, 2016

Currently I tried to add an EntityListener directly to the Customer Entity. Again this gives me a circular reference:

Circular reference detected for service "doctrine.orm.default_entity_manager", path: "doctrine.orm.default_entity_manager".

Any idea @vadim2404 @pkruithof ? It seemed to work for others:

Came from:

Also see:

@webdevilopers
Copy link
Author

webdevilopers commented Apr 26, 2016

I now inject the Container instead of the Document Manager - as recommended by @egeloen @intelligentbee :

        <service id="customer.listener" class="Acme\AppBundle\Event\BranchListener">
            <argument type="service" id="service_container" />
            <tag name="doctrine.orm.entity_listener" />
        </service>

Since Doctrine Bundle 1.6 this would also work with injecting the Document Manager WITHOUT getting a circular reference issue.

Unfortunately it does no longer work as soon I add a second service for the inverse side - a ODM listener that needs the Entity Manager:

        <service id="odm.doctrine.listener" class="Acme\AppBundle\Event\MongoEventSubscriber">
            <argument type="service" id="service_container"/>
            <tag name="doctrine_mongodb.odm.event_listener" event="postLoad"/>
        </service>

Result:

Circular reference detected for service "doctrine.orm.default_entity_manager", path: "cache_warmer -> twig -> security.authorization_checker -> security.authentication.manager -> fos_user.user_manager -> fos_user.entity_manager -> doctrine.orm.default_entity_manager -> doctrine.orm.default_entity_listener_resolver -> customer.listener -> doctrine_mongodb.odm.default_document_manager -> doctrine_mongodb.odm.default_connection -> doctrine_mongodb.odm.event_manager -> odm.doctrine.listener".

Unfortunately there does not seem to be an equivalent to the Entity Listener, I guess it would work with a Document Listener.
I guess the only way to solve this is overriding the Resolver?

Do you have a better idea @stof?

@webdevilopers
Copy link
Author

I think a good alternative would be to use the approach of the References listener by @jwage:
https://github.com/Atlantic18/DoctrineExtensions/blob/master/lib/Gedmo/References/ReferencesListener.php#L20-27

Instead that in my approach I don't want the $referencedObjectId field.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment