我正在尝试创建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源代码编写了大部分代码。但是我在这一点上陷入困境,因为我不知道为什么协会找不到欠款。
答案 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()
;