Symfony2 - Doctrine2: Cross-Database Join Column throws Mapping-Exception

喜欢而已 提交于 2019-12-01 08:02:58
Nate

It looks like this question is old, but was never answered. I'm hoping this answer helps fellow Googlers stumbling across this question.

You can't set up a direct relationship between entities across entity managers. You can set up relationships across bundles, though, if they share the same entity manager.

Relationships between 2 entities using the same entity manager [2.2+]:

Have a look at the Symfony docs on the issue

Essentially, in Bundle1, create an interface, then implement it on your entity. In Bundle2, link your @ManyToOne annotation to the interface instead of the entity itself. Then, tell Symfony in the config how to resolve the interface.

Bundle1:

<?php

// src/My/Bundle1/Entity/Bundle1Entity.php

namespace My\Bundle1\Entity;

use My\Bundle1\Entity\MyBundle2Entity; // <-- NOT NEEDED

interface Bundle1EntityInterface {}

class MyBundle1Entity implements Bundle1EntityInterface
{
    // ...
}

Bundle2:

<?php

// src/My/Bundle2/Entity/Bundle2Entity.php

namespace My\Bundle2\Entity;
class MyBundle2Entity
{
    // ...

    /**
     * @var MyBundle1Entity
     *
     * @ORM\ManyToOne(targetEntity="My\Bundle1\Entity\Bundle1EntityInterface")
     * @ORM\JoinColumn(name="my_bundle1_entity_id", nullable=true)
     */
    private $myBundle1Entity;
}

App Config:

# app/config/config.yml
doctrine:
    # ....
    orm:
        # ....
        resolve_target_entities:
            My\Bundle1\Entity\Bundle1EntityInterface: My\Bundle1\Entity\Bundle1Entity

Relationships between 2 entities using a different entity manager

Because the entities cannot be tied directly, you must hook into the postLoad event to set up the reference, while persisting the id manually. See docs for an example and explanation of blending a MongoDB object with an ORM object.

Here's a skeleton (getters/setters removed), using 2 entity managers:

Entities:

<?php

// src/My/Bundle1/Entity/Bundle1Entity.php

namespace My\Bundle1\Entity;

class MyBundle1Entity
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;
}


// src/My/Bundle2/Entity/Bundle2Entity.php

namespace My\Bundle2\Entity;
class MyBundle2Entity
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var integer
     *
     * @ORM\Column(type="integer")
     */
    private $myBundle1EntityId;

    /**
     * @var MyBundle1Entity
     */
    private $myBundle1Entity;

    public function setMyBundle1Entity($entity)
    {
        $this->myBundle1EntityId = $entity->getId();
        $this->myBundle1Entity = $entity;
    }
}

Event Listener:

<?php

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\LifecycleEventArgs;

class MyEventSubscriber
{
    public function __construct(EntityManager $bundle1Em)
    {
        $this->bundle1Em = $bundle1Em;
    }

    public function postLoad(LifecycleEventArgs $eventArgs)
    {
        $myBundle2Entity = $eventArgs->getEntity();
        $defaultEm = $eventArgs->getEntityManager();
        $myBundle2EntityReflProp = $defaultEm->getClassMetadata('Entity\MyBundle2Entity')
            ->reflClass->getProperty('myBundle1Entity');
        $myBundle2EntityReflProp->setAccessible(true);
        $myBundle2EntityReflProp->setValue(
            $myBundle1Entity, $this->bundle1Em->getReference('Entity\MyBundle1Entity', $myBundle2Entity->getMyBundle1Id())
        );
    }
}

Obviously, you'd have to register the event listener and pass bundle 1's entity manager as an argument.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!