动态映射manyToOne关系
我正在尝试创建Symfony Bundle,其中定义的实体可用于一对多/多对一的关系,而无需手动重写映射。动态映射manyToOne关系
我这样做是通过订阅loadClassMetadata
事件并添加基于它们实现的接口的映射。这并不像使用ResolveTargetEntityListener
那么简单,因为它只是简单地将接口替换为具体的类。
一个例子。我有一个地址和一个客户实体。客户有很多地址。 但是另一个包可能会重新定义客户(或者可能有多个地址的完全不同的实体)。为此,客户实施了AddressableInterface
。为了便于使用,我在特性中实现了这个接口。
在用户中,我检查该类是否实现了AddressableInterface
。如果是这样,则将OneToMany添加到Address
,并将ManyToOne添加到实现AddressableInterface
的类。 (在这个例子中Customer
类)
然而这留下了以下错误:
The association Entity\Customer#addresses refers to the owning side field Entity\Address#subject which does not exist.
但我设置到协会在我的用户两种方式。
下面是我的代码的本质。
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代码和教条捆绑源。但是我坚持这一点,因为我不知道协会为什么找不到欠款方。
您的地址类不具有主题字段的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()
;
$主题实际上是保护与getter和setter,但为了简洁,我留下了它们。 ManyToMany的任何具体原因,在我提到的文章中他们还定义了ManyToMany而不是OneToMany? – Waaghals 2014-10-08 12:22:30
如果允许标记与接口的关系,则允许地址与任何类关联。虽然您可以在给定类的LoadClassMetadata上动态引入OneToMany,但不能在数据库中动态更改OneToMany,这意味着您不能说“此时的地址与客户有关,所以'主题'是指客户并且在另一个执行更改它“ 你只能通过M2M实现它,并为每个关系地址创建一个连接表 another_class – 2014-10-08 12:26:22
尼斯的问题,我只是想知道,如果你真的需要反向关系。否则,您不需要增加“地址”的元数据。此外,如果多个实体在同一应用程序中具有地址(例如公司,人员),则它们将避免冲突,它们不能由“主体”映射)。 – fejese 2014-10-08 09:15:41
确实看起来不像我需要反向关系,当我删除“Address”的元数据时,错误仍然保持不变。 – Waaghals 2014-10-08 09:28:44
您是否已从'mapOneToMany'调用中移除'mappedBy'选项? – fejese 2014-10-08 09:31:46