{type}列在Doctrine类表继承中起什么作用?

时间:2019-04-08 12:17:55

标签: php symfony doctrine-orm polymorphic-associations

使用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个表:tagpost_tagcomment_tagpost_tag表结构如下:

id | post_id

tag表结构如下:

id | title | type

例如帖子和标签相关?如果我想将帖子13与标签test相关联,结果将是这样:

post_tag表:

id | post_id
------------
1  | 13

tag表:

id | title | type
-----------------
1  | test  | post

如果是这样,那么如果我想将同一标签(test)与注释相关联该怎么办。 tag表会是这样吗?

id | title | type
--------------------
1  | test  | post
2  | test  | comment

这似乎有点多余。然后,test表中的两行表示相同的实体(tag标记)。我搞错了吗?

2 个答案:

答案 0 :(得分:2)

tl; dr:继承是错误的工具。标签是标签是标签。继承是通过使用多个关联(多对多)而固有地提供的。

您的继承从本质上说:标签有两种不同的类型,它们本质上是不同的。可以将一个标签应用于帖子,将一个标签应用于评论,这些标签不是相同的标签,而是不同的标签。

由于两种标记都存储在同一张表中,因此必须存在 some 机制来区分一种标记。这就是类型列的用途。 (因此,这实质上是您对主要问题afaict的回答)

因此,从本质上讲,如果您想同时标记评论和博客(帖子),则这些是更常见的选项:

  1. 标记:(标记ID,标记名称等),注释标记:(标记ID,注释ID),博客标记:(标记ID,博客ID)... (我想,这就是您想要/需要的)
  2. 标签:相同,标签分配:(tag_id,object_type,object_id)... (在学说中不方便,总体上不利¹)

您选择了另一种方法:标签:(tag_id,tag_name,object_type),Tagassignments:(tag_id,object_id)(<-对象类型由tagid隐式给出,但是由于使用关系,Tagassignments被拆分了进入blog_tags和comment_tags)

但是,正如Magnus Eriksson正确评论的那样,这可能是有道理的。我有疑问我认为,选项1或选项2更为常见和方便。并且您应该将继承放在Tag上,而在关联上添加继承(如果需要,您需要使其成为一个额外的Entity才能起作用),而是建议选择选项1,因为这样做很容易实现学说及其注释。 (尽管您需要为应该标记的每种不同对象类型添加一个get {Object} s()。)

¹如马格努斯(Magnus)在下面的正确评论(并由我共同评论):您失去了数据库提供的大多数优势,主要是性能,清晰度和一致性。我通常会建议不要使用这种方法。

答案 1 :(得分:0)

For the sake of posterity, this is the approach I took to implement @Jakumi's answer:

  1. Scrap the existing tables.
  2. Create a Tag entity with the symfony console, with the following fields:

    a) post (type: relation; many-to-many)

    b) comment (type: relation; many-to-many)

    c) title (type: string)

  3. Make & run migrations.

This automatically created the tables tag_post and tag_comment.