2
votes

i'm following that recipe http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/cookbook/blending-orm-and-mongodb-odm.html#event-subscriber and when i get to the event subscriber i'm not able to inject the proper entity manager, the one named $this->dm initialized in the constructor.

As i understand, the entity manager used by the entity which is being loaded can be retrieved through $em = $eventArgs->getEntityManager(); then i need another one which i inject in the following manner:

    services:
        postload.listener:
        class: myVendor\myFooBarBundle\Listener\myEntityListener
        tags:
            - { name: doctrine.event_listener, event: postLoad }
        arguments:
            - "@doctrine.orm.foobar_entity_manager"

Those are my entity managers:

//orm.yml
        orm:
        entity_managers:
            default:
                connection: default
                mappings:
                    myVendormyFooBarBundle:
                        prefix: "myVendor\myFooBarBundle\Entity"
                        type: annotation
                        is_bundle: true
                        dir: "Entity"
            foobar:
                connection: foobar
                mappings:
                    myVendormyFooBarBundle:
                        prefix: "myVendor\myFooBarBundle\View"
                        type: annotation
                        is_bundle: true
                        dir: "View"

When injecting the foobar entity manager using the above strategy i get the following error:

Circular reference detected for service "postload.listener", path: "routing.loader -> routing.db.loader -> doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> postload.listener -> doctrine.orm.fooba_entity_manager -> doctrine.dbal.foobar_connection".  

That's myVendor\myFooBarBundle\Listener\myEntityListener class:

    class myFooBarEntityListener
    {

        public function __construct( \Doctrine\ORM\EntityManager $em )
        {
            $this->em = $em;
        }

        public function postLoad( LifecycleEventArgs $eventArgs )
        {
            $myEntity = $eventArgs->getEntity();

            if( $myEntity instanceof \myVendor\myFooBarBundle\Entity\myEntity )
            {
                $em = $eventArgs->getEntityManager();
                $fooBarReflProp = $em->getClassMetadata( 'myVendor\myFooBarBundle\Entity\myEntity' )->reflClass->getProperty( 'FooBarEntity' );
                $fooBarReflProp->setAccessible( true );
                $fooBarEntity = $this->em->getRepository( 'myVendor\myFooBarBundle\View\myFooBarEntity' )->findOneBy( array( 'id' => $myEntity->getFooBarEntityId() ) );
                $fooBarReflProp->setValue( $myEntity, $fooBarEntity );
            }
        }
    }

Also to avoid the circular reference error i've tried not to inject the foobar entity manager and get it through LifecycleEventArgs $eventArgs:

    class myFooBarEntityListener
    {

        public function postLoad( LifecycleEventArgs $eventArgs )
        {
            $myEntity = $eventArgs->getEntity();

            if( $myEntity instanceof \myVendor\myFooBarBundle\Entity\myEntity )
            {
                $em = $eventArgs->getEntityManager();
                $fooBarReflProp = $em->getClassMetadata( 'myVendor\myFooBarBundle\Entity\myEntity' )->reflClass->getProperty( 'FooBarEntity' );
                $fooBarReflProp->setAccessible( true );
                //NOTICE HOW HERE I SHOULD GET THE PROPER ENTITY MANAGER THROUGH $eventArgs
                $fooBarEntity = $eventArgs->getEntityManager('foobar')->getRepository( 'myVendor\myFooBarBundle\View\myFooBarEntity' )->findOneBy( array( 'id' => $myEntity->getFooBarEntityId() ) );
                $fooBarReflProp->setValue( $myEntity, $fooBarEntity );
            }
        }
    }

That last implementation throughs me the following error:

An exception has been thrown during the rendering of a template ("Class myVendor\myFooBarBundle\View\myFooBarEntity is not a valid entity or mapped super class.") in "SonataAdminBundle:CRUD:base_list.html.twig" at line 28.

The above error is caused by $fooBarEntity = $eventArgs->getEntityManager('foobar')->getRepository( 'myVendor\myFooBarBundle\View\myFooBarEntity' )->findOneBy( array( 'id' => $myEntity->getFooBarEntityId() ) ); because when i place echo 'hello';die(); just before that line the error is not thrown but when placed just after the line the error is thrown and hello is not shown. The error makes me think that although i'm explicitly getting the foobar connection through $eventArgs->getEntityManager('foobar') it is still giving me the default connection/entity manager.

In order to double check myVendor\myFooBarBundle\View\myFooBarEntity syntax i went to octrine\ORM\Mapping\Driver\DriverChain and placed the following code:

    if( strpos( $className, 'myFooBarEntity' ) )
    {
        echo 'Class: '.$className."\n\n";
        foreach ($this->_drivers as $namespace => $driver)
        {
            echo 'namespace: '.$namespace."\n";
            $bool = strpos($className, $namespace);
            var_dump($bool);
            echo "\n\n";
        }
    }
    die();

That DriverChain code gives me the following, that's why i think 'foobar' connection is never used or symfony has some kind of bug interpreting orm.yml file which defines the entity managers plus namespaces to use.

class: myVendor\myFooBarBundle\View\myFooBarEntity

namespace: myVendor\myFooBarBundle\Entity bool(false)

If i look foor the entity word inside myVendor\myFooBarBundle\View\myFooBarEntity i just find @ORM\Entity for the entity definition and also @ORM\OneToMany( targetEntity=.....) for the relation with another entity.

I hope someone can help, as it's driving me crazy. Thanks a lot!!

4

4 Answers

3
votes

I found a solution:

services:
    postload.listener:
    class: myVendor\myFooBarBundle\Listener\myEntityListener
    tags:
        - { name: doctrine.event_listener, event: postLoad }
    arguments:
        - @doctrine

My listener:

namespace myVendor\myFooBarBundle\Listener\myEntityListener;

use Symfony\Bundle\DoctrineBundle\Registry;

class myFooBarEntityListener
{

    private $reg;

    public function __construct(Registry $reg)
    {
        //dont't put your entitymanager otherwise a loop appear during creation
        $this->reg = $reg;
    }

    public function postLoad( LifecycleEventArgs $eventArgs )
    {
        $myEntity = $eventArgs->getEntity();

        if( $myEntity instanceof \myVendor\myFooBarBundle\Entity\myEntity )
        {

            $em = $this->reg->getEntityManager('not_default');
            $userPointdbManager = $em->getRepository('FullerUserBundle:UserPointdb');

            ....
        }
    }
}

You can use several entity managers now.

1
votes

I think I see your problem:

You're trying to work on Entites that are not managed by the entityManager you're working with.

The reason is that in your first example, you ony work on the service doctrine.orm.foobar_entity_manager, that is not aware of myVendor\myFooBarBundle\Entity entites.

In second one you try to access different entityManagers using: $eventArgs->getEntityManager('foobar') but this won't work. EventArgs is attached to only ONE entityManager, and there is no argument (like 'foobar') to access another one.

So the best solution I see here is to act like in your first idea, but injecting both entityMangers:

services:
    postload.listener:
    class: myVendor\myFooBarBundle\Listener\myEntityListener
    tags:
        - { name: doctrine.event_listener, event: postLoad }
    arguments:
        - "@doctrine.orm.default_entity_manager"
        - "@doctrine.orm.foobar_entity_manager"

If you have cyclic dependencies detection, try to inject the doctrine service, which is an instance of Symfony\Bridge\Doctrine\ManagerRegistry.

1
votes

Do not set Registry as parameter, but RegistryInterface (use Symfony\Bridge\Doctrine\RegistryInterface)

0
votes

The last example started to work for me after changing use Symfony\Bundle\DoctrineBundle\Registry; to use Doctrine\Bundle\DoctrineBundle\Registry;.

So it should be:

namespace myVendor\myFooBarBundle\Listener\myEntityListener;

use Doctrine\Bundle\DoctrineBundle\Registry

class myFooBarEntityListener
{

    private $reg;

    public function __construct(Registry $reg)
    {
        //dont't put your entitymanager otherwise a loop appear during creation
        $this->reg = $reg;
    }

    public function postLoad( LifecycleEventArgs $eventArgs )
    {
        $myEntity = $eventArgs->getEntity();

        if( $myEntity instanceof \myVendor\myFooBarBundle\Entity\myEntity )
        {

            $em = $this->reg->getEntityManager('not_default');
            $userPointdbManager = $em->getRepository('FullerUserBundle:UserPointdb');

            ....
        }
    }
}