Doctrine class table 继承中的 `type` 列起什么作用?
What is the role played by the `type` column in Doctrine class table inheritance?
使用 Symfony 4/Doctrine 2.6。我有两个实体 Post 和 Comment。我希望两者都可以标记。所以我创建了一个实体标签。我使用 Doctrine 的 class table inheritance 来创建关系:
/**
* @ORM\Entity(repositoryClass="App\Repository\TagRepository")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({"post" = "PostTag", "comment" = "CommentTag"})
*/
abstract class Tag
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $title;
// Getters and setters...
}
/** @ORM\Entity */
class PostTag extends Tag
{
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Post", inversedBy="tags")
*/
private $post;
public function getPost(): ?Post
{
return $this->post;
}
}
/** @ORM\Entity */
class CommentTag extends Tag
{
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Comment", inversedBy="comments")
*/
private $comment;
public function getComment(): ?Comment
{
return $this->comment;
}
}
这将创建 3 个 table:tag
、post_tag
和 comment_tag
。 post_tag
table 结构如下所示:
id | post_id
tag
table 结构如下所示:
id | title | type
例如posts和标签相关联?如果我想将 post 13
与标签 test
相关联,结果会是这样吗:
post_tag
table:
id | post_id
------------
1 | 13
tag
table:
id | title | type
-----------------
1 | test | post
?
如果是这样,那么如果我想将相同的标签 (test
) 与评论相关联怎么办? tag
table 会像这样吗?
id | title | type
--------------------
1 | test | post
2 | test | comment
这似乎有点多余。然后,相同的实体(test
标签)在 tag
table 中由 2 行表示。我理解错了吗?
tl;dr:继承是错误的工具。标签是标签是标签。继承是 固有地 通过使用多个关联(多对多)提供的。
你的继承本质上说:有两种截然不同的标签类型,它们在本质上是不同的。一个标签,可以应用到帖子,一个标签,可以应用到评论,它们不是同一个标签,而是不同的标签。
由于两种标签存储在同一个table中,因此必须有一些机制来区分它们。这就是类型列的用途。 (所以,这基本上就是你主要问题的答案,afaict)
所以基本上,如果你想标记评论和博客(帖子)一样,这些是更常见的选项:
- 标签:(tag_id,tag_name,随便),Comment_tags:(tag_id,comment_id),Blog_tags:( tag_id, blog_id) ... (我想,这就是你 want/need)
- 标签:相同,标签分配:(tag_id, object_type, object_id) ... (不方便在 Doctrine 中,总体不利¹)
您选择了不同的方法:标签:(tag_id, tag_name, object_type),标签分配:(tag_id, object_id) (< -- 对象类型由 tagid 隐式给出,但由于您使用的是关系,Tagassignments 被分成 blog_tags 和 comment_tags)
但是,正如 Magnus Eriksson 正确评论的那样,这可能是有道理的。我有我的疑问。我认为,选项 1 或选项 2 都更为常见和方便。而且你应该放弃标签上的继承,而是在关联上添加继承(如果需要,你需要让它成为一个额外的实体才能使其工作),而是建议选择选项 1,因为它更容易实现学说及其注释。 (尽管您需要为应该可标记的每种不同对象类型添加一个 get{Object}s()。)
¹正如 Magnus 在下面正确评论的那样(以及我的共同评论):你失去了数据库提供的大部分优势,主要是性能、清晰度和一致性。我通常会反对这种方法。
为了后代,这是我实现@Jakumi 回答的方法:
- 废弃现有表格。
使用 symfony 控制台创建一个 Tag
实体,包含以下字段:
a) post
(类型:关系;多对多)
b) comment
(类型:关系;多对多)
c) title
(类型:字符串)
- 进行并运行 迁移。
这会自动创建表 tag_post
和 tag_comment
。
使用 Symfony 4/Doctrine 2.6。我有两个实体 Post 和 Comment。我希望两者都可以标记。所以我创建了一个实体标签。我使用 Doctrine 的 class table inheritance 来创建关系:
/**
* @ORM\Entity(repositoryClass="App\Repository\TagRepository")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({"post" = "PostTag", "comment" = "CommentTag"})
*/
abstract class Tag
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $title;
// Getters and setters...
}
/** @ORM\Entity */
class PostTag extends Tag
{
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Post", inversedBy="tags")
*/
private $post;
public function getPost(): ?Post
{
return $this->post;
}
}
/** @ORM\Entity */
class CommentTag extends Tag
{
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Comment", inversedBy="comments")
*/
private $comment;
public function getComment(): ?Comment
{
return $this->comment;
}
}
这将创建 3 个 table:tag
、post_tag
和 comment_tag
。 post_tag
table 结构如下所示:
id | post_id
tag
table 结构如下所示:
id | title | type
例如posts和标签相关联?如果我想将 post 13
与标签 test
相关联,结果会是这样吗:
post_tag
table:
id | post_id
------------
1 | 13
tag
table:
id | title | type
-----------------
1 | test | post
?
如果是这样,那么如果我想将相同的标签 (test
) 与评论相关联怎么办? tag
table 会像这样吗?
id | title | type
--------------------
1 | test | post
2 | test | comment
这似乎有点多余。然后,相同的实体(test
标签)在 tag
table 中由 2 行表示。我理解错了吗?
tl;dr:继承是错误的工具。标签是标签是标签。继承是 固有地 通过使用多个关联(多对多)提供的。
你的继承本质上说:有两种截然不同的标签类型,它们在本质上是不同的。一个标签,可以应用到帖子,一个标签,可以应用到评论,它们不是同一个标签,而是不同的标签。
由于两种标签存储在同一个table中,因此必须有一些机制来区分它们。这就是类型列的用途。 (所以,这基本上就是你主要问题的答案,afaict)
所以基本上,如果你想标记评论和博客(帖子)一样,这些是更常见的选项:
- 标签:(tag_id,tag_name,随便),Comment_tags:(tag_id,comment_id),Blog_tags:( tag_id, blog_id) ... (我想,这就是你 want/need)
- 标签:相同,标签分配:(tag_id, object_type, object_id) ... (不方便在 Doctrine 中,总体不利¹)
您选择了不同的方法:标签:(tag_id, tag_name, object_type),标签分配:(tag_id, object_id) (< -- 对象类型由 tagid 隐式给出,但由于您使用的是关系,Tagassignments 被分成 blog_tags 和 comment_tags)
但是,正如 Magnus Eriksson 正确评论的那样,这可能是有道理的。我有我的疑问。我认为,选项 1 或选项 2 都更为常见和方便。而且你应该放弃标签上的继承,而是在关联上添加继承(如果需要,你需要让它成为一个额外的实体才能使其工作),而是建议选择选项 1,因为它更容易实现学说及其注释。 (尽管您需要为应该可标记的每种不同对象类型添加一个 get{Object}s()。)
¹正如 Magnus 在下面正确评论的那样(以及我的共同评论):你失去了数据库提供的大部分优势,主要是性能、清晰度和一致性。我通常会反对这种方法。
为了后代,这是我实现@Jakumi 回答的方法:
- 废弃现有表格。
使用 symfony 控制台创建一个
Tag
实体,包含以下字段:a)
post
(类型:关系;多对多)b)
comment
(类型:关系;多对多)c)
title
(类型:字符串)- 进行并运行 迁移。
这会自动创建表 tag_post
和 tag_comment
。