我有几个带标签的实体。我已根据此blog post:
创建了动态关系class TaggableListener implements EventSubscriber
{
/**
* @return array
*/
public function getSubscribedEvents()
{
return [
Events::loadClassMetadata
];
}
/**
* @param \Doctrine\ORM\Event\LoadClassMetadataEventArgs $eventArgs
*/
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
// the $metadata is the whole mapping info for this class
$metadata = $eventArgs->getClassMetadata();
$class = $metadata->getReflectionClass();
if (!$class->implementsInterface(TaggableEntityInterface::class)) {
return;
}
$namingStrategy = $eventArgs
->getEntityManager()
->getConfiguration()
->getNamingStrategy()
;
$metadata->mapManyToMany([
'targetEntity' => Tag::class,
'fieldName' => 'tags',
'cascade' => ['persist'],
'joinTable' => [
'name' => $namingStrategy->classToTableName($metadata->getName()) . '__Tags',
'joinColumns' => [
[
'name' => $namingStrategy->joinKeyColumnName($metadata->getName()),
'referencedColumnName' => $namingStrategy->referenceColumnName(),
'onDelete' => 'CASCADE',
'onUpdate' => 'CASCADE',
],
],
'inverseJoinColumns' => [
[
'name' => 'tag_id',
'referencedColumnName' => $namingStrategy->referenceColumnName(),
'onDelete' => 'CASCADE',
'onUpdate' => 'CASCADE',
],
]
]
]);
}
}
interface TaggableEntityInterface
{
public function addTag(Tag $tags);
public function removeTag(Tag $tags);
public function getTags();
}
trait Tags
{
protected $tags;
public function addTag(Tag $tags) { /*..*/ };
public function removeTag(Tag $tags) { /*..*/ };
public function getTags() { /*..*/ };
}
我可以使用$category->getTags()
或$product->getTags()
获取所有代码。这非常有效。这是一篇很老的博文,但显然没有过时。
但由于没有Tag::getProducts()
方法,因此没有简单的方法可以获得与标签相关的所有产品。
理想情况下,我希望Tag::getRelatedEntities()
返回TaggableEntityInterface[]
,因此该集合包含Product
和Category
个实体。
在我的模板中,我想到了这样的事情:
<h1>Related entities:</h1>
<ul>
{% for relatedEntity in tag.getRelatedEntities %}
<li>{{ relatedEntity }}</li> //..implementing `__toString()`
{% endfor %}
</ul>
获得Tag::getRelatedEntities()
和/或Tag::getProducts()
方法的最佳方法是什么?
当然,我可以在我的Tag实体上手动创建getProducts
方法,但这种方法打破了动态关系的想法。在这个例子中,我只有一个产品和类别实体,但实际上我有许多实体是Taggable。
答案 0 :(得分:2)
您无法在Doctrine中创建指向多个不同实体的单一关系。换句话说,如果Tag
具有relatedEntities
,则它必须是同一类的对象数组或至少是映射的超类。
你能做的是编写一个迭代所有Doctrine托管类的服务,检查它是否实现了给定的接口,按标签搜索这些实体并将它们全部合并到一个数组中。
示例代码:
$entities = [];
foreach ($entityManager->getMetadataFactory()->getAllMetadata() as $classMetadata) {
if (in_array(TaggableEntityInterface::class, class_implements($classMetadata->getName()))) {
$repository = $entityManager->getRepository($classMetadata->getName());
$entities = array_merge($entities, $repository->findBy(['tag' => $tag]));
}
}
如果您想从模板轻松访问此功能,也可以将其包装成twig函数。
代码未经过实际测试,因此根据您的使用情况,可能需要进行一些更正或改进。