我有几个带标签的实体。我在这个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[]
,因此Collection包含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。
您无法在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函数中。
代码未经过实际测试,因此根据您的使用情况,可能需要进行一些更正或改进。