2 votes

Symfony2/Doctrine2 : Problèmes de gestion de plusieurs gestionnaires d'entités dans un listener

Je suis cette recette http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/cookbook/blending-orm-and-mongodb-odm.html#event-subscriber et quand j'arrive à l'abonné de l'événement je ne suis pas capable d'injecter le bon entity manager celui qui est nommé $this->dm initialisé dans le constructeur.

D'après ce que j'ai compris, le gestionnaire d'entités utilisé par l'entité en cours de chargement peut être récupéré par le biais de la fonction $em = $eventArgs->getEntityManager(); alors j'en ai besoin d'un autre que je inject de la manière suivante :

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

Ce sont mes gestionnaires d'entités :

//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"

Quand injecting le site foobar entity manager En utilisant la stratégie ci-dessus, j'obtiens l'erreur suivante :

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".  

C'est myVendor\myFooBarBundle\Listener\myEntityListener classe :

    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 );
            }
        }
    }

Aussi pour éviter le circular reference error J'ai essayé not pour injecter le foobar entity manager et le faire passer 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 );
            }
        }
    }

Cette dernière mise en œuvre m'amène à l'erreur suivante :

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.

L'erreur ci-dessus est causée par $fooBarEntity = $eventArgs->getEntityManager('foobar')->getRepository( 'myVendor\myFooBarBundle\View\myFooBarEntity' )->findOneBy( array( 'id' => $myEntity->getFooBarEntityId() ) ); parce que lorsque je place echo 'hello';die() ; juste avant cette ligne, l'erreur n'est pas déclenchée, mais lorsqu'elle est placée juste après la ligne, l'erreur est déclenchée. hello n'est pas indiqué. L'erreur me fait penser que, bien que je reçoive explicitement le message d'erreur de l'utilisateur, il n'y a pas de réponse. foobar connexion par $eventArgs->getEntityManager('foobar') il me donne toujours le default connection/entity manager .

Afin de revérifier myVendor\myFooBarBundle\View\myFooBarEntity la syntaxe que je suis allé voir octrine\ORM\Mapping\Driver\DriverChain et placé le code suivant :

    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();

Ce code DriverChain me donne ce qui suit, c'est pourquoi je pense que la connexion 'foobar' n'est jamais utilisée ou que Symfony a une sorte de bug d'interprétation. orm.yml qui définit les gestionnaires d'entités et les espaces de noms à utiliser.

classe : monVendeur \myFooBarBundle\View\myFooBarEntity

espace de noms : myVendor \myFooBarBundle\Entity bool(false)

Si je cherche le entity mot à l'intérieur myVendor\myFooBarBundle\View\myFooBarEntity Je viens de trouver @ORM\Entity pour la définition de l'entité et aussi @ORM\OneToMany( targetEntity=.....) pour la relation avec une autre entité.

J'espère que quelqu'un pourra m'aider, car cela me rend fou. Merci beaucoup !

3voto

francis Points 31

J'ai trouvé une solution :

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

Mon auditeur :

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');

            ....
        }
    }
}

Vous pouvez désormais utiliser plusieurs gestionnaires d'entités.

1voto

Florian Points 5388

Je crois que je vois votre problème :

Vous essayez de travailler sur des entités qui ne sont pas gérées par le gestionnaire d'entités avec lequel vous travaillez.

La raison est que dans votre premier exemple, vous ne travaillez que sur le service doctrine.orm.foobar_entity_manager qui n'est pas conscient de myVendor\myFooBarBundle\Entity les droits.

Dans le deuxième cas, vous essayez d'accéder à différents entityManagers en utilisant : $eventArgs->getEntityManager('foobar') mais ça ne marchera pas. EventArgs est attaché à un seul entityManager, et il n'y a pas d'argument (comme 'foobar') pour accéder à un autre.

La meilleure solution que je vois ici est donc d'agir comme dans votre première idée, mais en injectant les deux gestionnaires d'entités :

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"

Si vous avez une détection de dépendances cycliques, essayez d'injecter le fichier doctrine qui est une instance de Symfony\Bridge\Doctrine\ManagerRegistry .

1voto

Florent Morselli Points 536

Ne pas définir Registry comme paramètre, mais RegistryInterface (utiliser Symfony \Bridge\Doctrine\RegistryInterface )

0voto

Vasili Pascal Points 9

Le dernier exemple a commencé à fonctionner pour moi après avoir modifié utiliser Symfony \Bundle\DoctrineBundle\Registry ; utiliser Doctrine \Bundle\DoctrineBundle\Registry ;.

Il devrait en être ainsi :

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');

            ....
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X