动态映射manyToOne关系

时间:2014-10-08 09:10:21

标签: symfony doctrine-orm

我正在尝试创建Symfony Bundle,其中定义了实体,可以在一对多/多对一关系中使用,而无需手动重写映射。

我这样做是通过订阅loadClassMetadata事件并根据它们实现的接口添加映射。它并不像使用ResolveTargetEntityListener那么简单,因为它只是用具体类替换接口。

一个例子。我有一个地址和一个客户实体。客户有很多地址。 但是另一个捆绑包可能会重新定义客户(或者可以具有多个地址的完全不同的实体)。因此,Customer实现了AddressableInterface。为了便于使用,我已经在一个特性中实现了这个接口。

在订阅者中,我检查该类是否实现了AddressableInterface。如果是这样,它会向Address添加OneToMany,并向实现AddressableInterface的类添加ManyToOne。 (在此示例中为Customer类)

然而,这会留下以下错误:

  

关联实体\客户#地址是指不存在的拥有方字段实体\地址#主题。

但我设置了在我的订阅者中以两种方式关联。

以下是我的代码的精髓。

namespace Entity;
class Address
{
    public $subject;
}

namespace Entity;
class Customer implements AddressableInterface
{
    use Traits/Addressable;
}

namespace Traits;
trait Addressable //Implements all methods from AddressableInterface
{
    protected $addresses;

    public function getAddresses()
    {
        return $this->addresses;
    }

    public function addAddress(AddressInterface $address)
    {
        $this->addresses->add($address);
    }

    public function removeAddress(AddressInterface $address)
    {
        $this->addresses->removeElement($address);
    }
 } 

活动订阅者

class DynamicAddressBindingSubscriber implements EventSubscriber
{
    public function getSubscribedEvents()
    {
        return [Events::loadClassMetadata];
    }

    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        $metadata = $eventArgs->getClassMetadata();

        $class = $metadata->getReflectionClass();
        if (!in_array(AddressableInterface::class, $class->getInterfaceNames())) {
            return;
        }

        $factory = new \Doctrine\ORM\Mapping\ClassMetadataFactory;
        $factory->setEntityManager($eventArgs->getEntityManager());
        $addressMetadata = $factory->getMetadataFor(Address::class);

        $addressMetadata->mapManyToOne(
            [
                "targetEntity" => $class->getName(),
                "fieldName"    => "subject",
                "inversedBy"   => "addresses"
            ]
        );

        $metadata->mapOneToMany(
            [
                'targetEntity' => Address::class,
                'fieldName'    => 'addresses',
                'mappedBy'     => 'subject'
            ]
        );
    }
}

我查看了多个示例,并根据this article和Doctrine Bundle源代码编写了大部分代码。但是我在这一点上陷入困​​境,因为我不知道为什么协会找不到欠款。

1 个答案:

答案 0 :(得分:3)

您的地址类没有主题字段的getter / setter。

另一件事是,如果你想将地址绑定到任何类,你可能更喜欢使它成为manyToMany关系。我是这样做的附件:

$metadata->mapManyToMany([
    'targetEntity' => '...\FilesBundle\Entity\Attachment',
    'fieldName' => 'attachments',
    'cascade' => array('persist'),
    'joinTable' => array(
        'name' => strtolower($namingStrategy->classToTableName($metadata->getName())) . '_attachment',
        'joinColumns' => array(
            array(
                'name' => $namingStrategy->joinKeyColumnName($metadata->getName()),
                'referencedColumnName' => $namingStrategy->referenceColumnName(),
                'onDelete' => 'CASCADE',
                'onUpdate' => 'CASCADE',
            ),
        ),
        'inverseJoinColumns' => array(
            array(
                'name' => 'file_id',
                'referencedColumnName' => $namingStrategy->referenceColumnName(),
                'onDelete' => 'CASCADE',
                'onUpdate' => 'CASCADE',
            ),
        )
    )
]);

其中namingStrategy来自事件:

$namingStrategy = $eventArgs
        ->getEntityManager()
        ->getConfiguration()
        ->getNamingStrategy()
;