Symfony2 - Doctrine2: Cross-Database Join Column t

2019-02-18 16:30发布

Hi want to make a join between two entities. The entities are in different databases:

Here is how I set up my database config:

doctrine:
    dbal:
    default_connection: default
    connections:
        default:
            driver:   %database_driver%
            host:     %database_host%
            port:     %database_port%
            dbname:   %database_name%
            user:     %database_user%
            password: %database_password%
            charset:  UTF8
            mapping_types:
                enum: string
        data_warehouse:
            driver:   %database_data_warehouse_driver%
            host:     %database_data_warehouse_host%
            port:     %database_data_warehouse_port%
            dbname:   %database_data_warehouse_name%
            user:     %database_data_warehouse_user%
            password: %database_data_warehouse_password%
            charset:  UTF8
            mapping_types:
                enum: string

    orm:
    auto_generate_proxy_classes: %kernel.debug%

    default_entity_manager: default

    entity_managers:
        default:
            connection: default
            mappings:
                MyBundle1: ~


        data_warehouse:
            connection: data_warehouse
            mappings:
                MyBundle2: ~

And these are my entities:

namespace My\Bundle1\Entity;
use My\Bundle1\Entity\MyBundle2Entity;
class MyBundle1Entity
{

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


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 MyBundle1Entity
     *
     * @ORM\ManyToOne( targetEntity="My\Bundle1\Entity\MyBundle1Entity")
     * @ORM\JoinColumn(name="my_bundle1_entity_id",  nullable=true)
     */
    private $myBundle1Entity;
}

When I try to use the doctrine:schema:update command, I get an error:

php app/console doctrine:schema:create --dump-sql --em=data_warehouse

Error:

[Doctrine\Common\Persistence\Mapping\MappingException]
The class 'My\Bundle1\Entity\Bundle1Entity' was not found in the chain configured namespaces My\Bundle2\Entity\Bundle2Entity

Is my setup correct or am I doeing something completely wrong? I assume that I define two entity managers and there connections and tell them, what bundles they have to handle. I ensure that there are only entities from one database in each bundle.

Thanks for your help

1条回答
叛逆
2楼-- · 2019-02-18 17:12

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.

查看更多
登录 后发表回答