当referencedColumnName与“id”

时间:2016-05-21 22:09:52

标签: symfony doctrine-orm

我正在使用我的第一个可分发的包中,我对如何在我的包中重用“存在的用户实体”有一些疑问。我的捆绑包的目的是将故障单实体与用户实体相关联。

当TargetEntity的'referencedColumnName'与'id'不同时,我正试图在不同的bundle之间关联实体。

我正在关注Resolve Target Entity Docs并且对我有用只有当“targetEntity”的主键被命名为“id”时才能正常工作。

示例:

// file: vendor\Kdrmklabs\Bundle\TicketBundle\Entity\Ticket.php

class Ticket {

    /**
     * @ORM\ManyToOne(targetEntity="\Kdrmklabs\Bundle\TicketBundle\Model\UserInterface")
     */
    private $user;
}
// file: vendor\Kdrmklabs\Bundle\TicketBundle\Model\UserInterface.php;

interface UserInterface {

    public function getId();

}
# file: app/config/config.yml

doctrine:
    orm:
        auto_mapping: true
        resolve_target_entities:
            Kdrmklabs\Bundle\TicketBundle\Model\UserInterface: AppBundle\Entity\User

如果实体AppBundle/Entity/User的主键名为“id”,则我的捆绑包与此实体之间的关系可以正常工作。

// file: src/AppBundle/Entity/User.php

class User implements \Kdrmklabs\Bundle\TicketBundle\Model\UserInterface
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
}

但是否则会发生异常:

// file: src/AppBundle/Entity/User.php

class User implements \Kdrmklabs\Bundle\TicketBundle\Model\UserInterface
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id_customer", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
}
Column name 'id' referenced for relation from \Kdrmklabs\Bundle\TicketBundle\Entity\Ticket towards AppBundle\Entity\User does not exists.

我知道如果你在注释@ORM\JoinColumn(referencedColumnName="id_of_the_external_user_entity")中指定外键的值,这将很有效,但是从我的包中我无法通过实际配置知道外键的名称。

  1. 有没有办法在两个实体之间创建关系,而不管这些主键的名称是什么?

  2. 可能是我可以请求用户在app / config / config.yml中指定他的主键名称“用户实体”并以某种方式指定这个值为doctrine来创建正确的关系我的包的实体和安装我的包的用户的实体。但是,我该怎么做呢?

  3. 谢谢。 问候

    提前多多感谢!!

    更多详情:

    我的可分发包的存储库是:https://github.com/KdrMkLabs/TicketBundle

1 个答案:

答案 0 :(得分:1)

在分析了这个问题几个小时之后,我得出结论,在彼此不知道的实体之间创建关系的正确方法是使用doctrine eventlisteners。我将在下面解释如何做到这一点。

您可以使用Doctrine事件侦听器解决冲突,以使用PHP而不是注释来映射实体。

  

EntityManager和UnitOfWork在其注册实体的生命周期内触发一系列事件。

Here you have a list of Lifecycle Events that you can use

在这种情况下,我们将监听事件loadClassMetadata以进行正确的学说映射,并在用户实体和捆绑实体之间建立良好的关系。

<强> 1。在捆绑包中创建Doctrine EventListener

// file: Kdrmklabs\Bundle\TicketBundle\EventListener\LoadMetadata.php

namespace Kdrmklabs\Bundle\TicketBundle\EventListener;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;

class LoadMetadata {
    protected $userRepository;
    protected $primary_key;

    public function __construct($userRepository, $primary_key)
    {
        $this->userRepository = $userRepository;
        $this->primary_key = $primary_key;
    }

    public function getSubscribedEvents()
    {
        return ['loadClassMetadata',];
    }

    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        $classMetadata = $eventArgs->getClassMetadata();
        $class_name = $classMetadata->getName();

        if($class_name == "Kdrmklabs\Bundle\TicketBundle\Entity\Ticket") {

            // The following is to map ORM with PHP
            $mapping = array(
                'targetEntity' => $this->userRepository,
                'fieldName' => 'user',
                'joinColumns' => array(
                    array(
                        'name' => 'user_id',
                        'referencedColumnName' => $this->primary_key
                    )
                )
            );

            $classMetadata->mapManyToOne($mapping);
        }
    }
}

请注意,'fieldName'的值对应于引用Ticket实体中$ user的class属性的名称。上面的映射数组与使用注释的doctrine ORM映射类似,如下所示:

class Ticket {

    /**
     * @ORM\ManyToOne(targetEntity="\Kdrmklabs\Bundle\TicketBundle\Model\UserInterface")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="?id_from_external_entity?")
     */
    private $user;
}

Here is more information about mapping in doctrine 2 with PHP

<强> 2。将eventListener注册为服务

# file: Kdrmklabs\Bundle\TicketBundle\Resources\config\services.yml

services:
    kdrmklabs_ticket.listener:
        class: Kdrmklabs\Bundle\TicketBundle\EventListener\LoadMetadata
        arguments:
            - %kdrmklabs_ticket.model.user.class%
            - %kdrmklabs_ticket.model.user.primary_key%
        tags:
            - { name: doctrine.event_listener, event: loadClassMetadata }

不要忘记从 DependencyInjection 中注入新参数kdrmklabs_ticket.model.user.primary_key,例如:

// file: Kdrmklabs\Bundle\TicketBundle\DependencyInjection\Configuration.php
class Configuration implements ConfigurationInterface
{
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder();
        $rootNode = $treeBuilder->root('kdrmklabs_ticket');

        $rootNode->children()
            ->scalarNode('user_class')->isRequired()->cannotBeEmpty()->end()
            ->scalarNode('user_primay_key')->isRequired()->cannotBeEmpty()->end()
        ->end();

        return $treeBuilder;
    }
}
// file: Kdrmklabs\Bundle\TicketBundle\DependencyInjection\KdrmklabsTicketExtension.php

class KdrmklabsTicketExtension extends Extension
{
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');

        $container->setParameter('kdrmklabs_ticket.model.user.class', $config['user_class']);
        $container->setParameter('kdrmklabs_ticket.model.user.primary_key', $config['user_primay_key']);
    }
}

第3。最后,从项目config.yml文件中注入参数'user_primay_key'。

# file: app\config\config.yml

kdrmklabs_ticket:
    user_class: AppBundle\Entity\User
    user_primay_key: id

就是这样。每当您更新学说方案时,Doctrine都会自动创建Ticket实体与项目config.yml(app / config / config.yml)中指定的外部User实体之间的关系