使用多个实体管理器解析目标实体

apf*_*pfz 6 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...
Run Code Online (Sandbox Code Playgroud)

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

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

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

基本上我想将用户从可重用的包中耦合到一个人.

我在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: ~
Run Code Online (Sandbox Code Playgroud)

此外,主包中的用户类扩展了供应商包中的基本用户.用户类当然是在主应用程序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 
Run Code Online (Sandbox Code Playgroud)

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

qoo*_*mao 3

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

AppBundle\Doctrine\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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

My\VendorBundle\Entity\User

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

AppBundle\Resources\services.yml

app.listener.insert_person:
    class: AppBundle\Doctrine\InsertPersonListener
    arguments:
        - '@doctrine'
    tags:
        - { name: doctrine.event_listener, event: postLoad }
Run Code Online (Sandbox Code Playgroud)

更新

在现实生活中寻找这样的例子后,我发现了这个。由此我看到你可以改变

$reflectionClass = new \ReflectionClass(User::class)
Run Code Online (Sandbox Code Playgroud)

$reflectionClass = $args->getObjectManager()
    ->getClassMetadata(User::class)
    ->reflClass
Run Code Online (Sandbox Code Playgroud)

我认为这意味着new \ReflectionClass(...)每次加载会减少一次调用。