使用多个实体经理

时间:2016-03-10 14:59:43

标签: symfony doctrine-orm

是否可以通过多个实体管理器解析目标实体?

我有一个班级人员(在一个可重复使用的包中):

/**
 *
 * @ORM\Entity
 * @ORM\Table(name="my_vendor_person")
 */
class Person
{ 
    /**
     * Unique Id
     *
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * First Name
     *
     * @var string $name
     *
     * @ORM\Column(name="first_name", type="string", length=32)
     */
    protected $firstName;
    // etc...

一个类用户(在我的主应用程序中):

/**
 * @ORM\Entity
 *
 * @ORM\Table(name="my_financial_user")
 *
 */
class User extends BaseUser
{

    /**
     * @ORM\OneToOne(targetEntity="My\FinancialBundle\Model\PersonInterface")
     * @var PersonInterface
     */
    protected $person;

基本上我想将用户从可重复使用的捆绑包中耦合到一个人。

我在doctrine的配置中设置了解决目标实体选项,我认为它允许我这样做:

doctrine:
  orm:
      auto_generate_proxy_classes: "%kernel.debug%"
      default_entity_manager: default
      resolve_target_entities:
          My\FinanceBundle\Model\PersonInterface: My\VendorBundle\Entity\Person
      entity_managers:
          default:
              naming_strategy: doctrine.orm.naming_strategy.underscore
              connection: default
              mappings:
                  MyFinanceBundle: ~
          second:
              naming_strategy: doctrine.orm.naming_strategy.underscore
              auto_mapping: false
              connection: second
              mappings:
                  MyVendorBundle: ~
                  MyVendorUserBundle: ~

此外,主包中的用户类扩展了供应商包中的基本用户。用户类当然是在主应用程序db中维护的。

使用此配置会给我一个错误。

[Doctrine\Common\Persistence\Mapping\MappingException]                                                                                       
The class 'My\VendorBundle\Entity\Person' was not found in the chain configured namespaces My\FinanceBundle\Entity, FOS\UserBundle\Model 

有谁知道如何解决这个问题?

1 个答案:

答案 0 :(得分:3)

正如评论中所提到的,实体经理不会互相交谈,因此您必须解决这个问题。一种方法是使用一个doctrine监听器并插入一个可以由getPerson方法调用的回调。

的appbundle \学说\ InsertPersonListener

use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use My\FinancialBundle\Entity\Person;
use My\VendorBundle\Entity\User;
use Symfony\Bridge\Doctrine\RegistryInterface;

class InsertPersonListsner
{
    /**
     * @var RegistryInterface
     */
    private $registry;

    /**
     * Insert the registry into the listener rather than the entity manager
     * to avoid a cyclical dependency issue
     *
     * @param RegistryInterface $registry
     */
    public function __construct(RegistryInterface $registry)
    {
        $this->registry = $registry;
    }

    /**
     * On postLoad insert person callback
     *
     * @var LifecycleEventArgs $args
     */
    public function postLoad(LifecycleEventArgs $args)
    {
        $user = $args->getObject();

        // If not a user entity ignore
        if (!$user instanceof User) {
            return;
        }

        $reflectionClass = new \ReflectionClass(User::class);

        $property = $reflectionClass->getProperty('personId');
        $property->setAccessible(true);

        // if personId is not set ignore
        if (null === $personId = $property->getValue($user)) {
            return;
        }

        // get the repository for your person class
        // - changed to your version from the comments
        $repository = $this->registry
            ->getManagerForClass(Person::class)
            ->getRepository('MyVendorBundle:Person');

        // set the value as a callback rather than the entity
        // so it's not called unless necessary
        $property = $reflectionClass->getProperty('personCallback');
        $property->setAccessible(true);
        $property->setValue($user, function() use ($repository, $personId) {
            return $repository->find($personId);
        });
    }
}

我\ VendorBundle \实体\用户

use My\FinancialBundle\Entity\Person;

class User
{
    //..

    /**
     * @var Person
     */
    private $person;

    /**
     * @var integer
     */
    private personId;

    /**
     * @var callable
     */
    private $personCallback;

    /**
     * Set person
     *
     * @param Person|null $person
     * @return $this
     */
    public function setPerson(Person $person = null)
    {
        $this->person = $person;

        // if the person is null reset the personId and
        // callback so that it's not called again
        if (null === $person) {
            $this->personId = null;
            $this->personCallback = null;
        } else {
            // set the personId to be stored on the db for the next postLoad event
            $this->personId = $person->getId();
        }

        return null;
    }

    /**
     * Get person
     *
     * @return Person|null
     */
    public function getPerson()
    {
        // if the person has not been set and the callback is callable,
        // call the function and set the result (of $repository->find($personId))
        // to the person property and then return it. The callback is
        // reset to stop unnecessary repository calls in case of a null response 
        if (null === $this->person && is_callable($this->personCallback)) {
            $this->person = call_user_func($this->personCallback);
            $this->personCallback = null;
        }

        return $this->person;
    }
}

的appbundle \资源\ services.yml

app.listener.insert_person:
    class: AppBundle\Doctrine\InsertPersonListener
    arguments:
        - '@doctrine'
    tags:
        - { name: doctrine.event_listener, event: postLoad }

<强>更新

在现实生活中寻找一个这样的例子后,我找到了this。从这里我看到你可以改变

$reflectionClass = new \ReflectionClass(User::class)

$reflectionClass = $args->getObjectManager()
    ->getClassMetadata(User::class)
    ->reflClass

我认为,这意味着每次加载会少new \ReflectionClass(...)次呼叫。