双向多与多实体之间的多



请帮我想想这个我已经忽视多年的问题,而且我做的时间越长,就越觉得我做错了。

RDBMS中,使用Doctrine ORM,我需要对各种实体执行任务。我必须能够选择实体匹配给定的任务。我还希望能够查询哪些实体正在使用哪些任务。

想象一个经典的ManyToMany双向关系,比如TasksProjects,用这样的存根代码:

/**
* @ORMEntity()
*/
class Project
{
/**
* @ORMManyToMany(targetEntity="Task", inversedBy="projects")
*/
protected $tasks;
}
/**
* @ORMEntity()
*/
class Task
{
/**
* @ORMManyToMany(targetEntity="Project", mappedBy="tasks")
*/
protected $projects;
}

一切正常,但这是一个只有两个实体的简单场景。

问题是:如果我有大量像Project这样的实体(例如:User, Company,…),我希望能够将它们与Task对象(方向"各种实体")关联起来,该怎么办?任务"),同时保持查看列表"所有任务"在一个地方,以及显示到任务所属的关联实体的链接的能力(项目,用户,公司,…)

我目前的方法是:我在(a)每次我想建立一个新的双向关系时向我的Task实体添加一个新属性,或者(b)我坚持使用一个单向关系,这让我可以将一个Task分配给任何数量的另一边实体,但不让我容易地计算出与哪个另一边实体相关的任务。

问题我看到:考虑100个必须与Task有关系的其他端实体。我是否必须创建链接Task->OtherSideEntity的100个ManyToMany属性?在查看"任务详细信息"时屏幕上,我怎么知道是哪个"另一边实体"呢?与任务有关吗?

我可以创建一个包含另一边实体名称的字符串属性,一旦我获取Task属性,我就可以在单独的查询中获取另一边,但这感觉非常笨拙。

我一定是在瞎说,这一定是一个普遍问题。这里的最佳实践是什么?我错过了什么?Doctrine/RDBMS是否不适合取系统中大量不相交的对象?

感谢您的阅读。

可以拆分多对多关系,并将关系表作为实体。我个人从不使用多对多关系,因为大多数时候你需要向关系中添加一些元数据。

这里是一个例子,其中articlephoto实体可以用同一组标签来标记。

class Article
{
/**
* @var ArticleTag[]
*
* @ORMOneToMany(targetEntity="ArticleTag", mappedBy="article")
*/
private $tags;
}
class ArticleTag
{
/**
* @var Article
*
* @ORMId()
* @ORMManyToOne(targetEntity="Article", inversedBy="tags")
*/
private $article;

/**
* @var Tag
*
* @ORMId()
* @ORMManyToOne(targetEntity="Tag")
*/
private $tag;
}
class Photo 
{
/**
* @var PhotoTag[]
*
* @ORMOneToMany(targetEntity="PhotoTag", mappedBy="photo")
*/
private $tags;
}
class PhotoTag
{
/**
* @var Photo
*
* @ORMId()
* @ORMManyToOne(targetEntity="Photo", inversedBy="tags")
*/
private $photo;

/**
* @var Tag
*
* @ORMId()
* @ORMManyToOne(targetEntity="Tag")
*/
private $tag;
}
class Tag
{
private string $title;
}

关系表包含复合主键,只保留唯一对。使用这种设计,您只需要为每个可以标记的实体创建关系表。

你可以用一个额外的getter来检索标签:

foreach ($photo->getTags() as $tagRelation) {
echo $tagRelation->getTag()->getTitle();
}

您可以检索包含特定标记的所有实体:

$this->entityManger->createQuery('SELECT entity FROM Article entity INNER JOIN entity.tags tags WHERE tags.tag = :tag');
$this->entityManger->createQuery('SELECT entity FROM Photo entity INNER JOIN entity.tags tags WHERE tags.tag = :tag');

最新更新