Doctrine - 一个带有多个实体标签的表格

Dai*_*mos 2 symfony doctrine-orm

我想创建带有分类词汇表的 Tag 实体类,它将被多个实体使用。有没有可能,用三列创建多条关系?我需要存储标签 id、实体 id 和实体类名。

所以我现在有:

<?php
/**
 * Taxonomy
 *
 * @ORM\Table(
 *    name="tag_taxonomy",
 *    indexes={
 *        @ORM\Index(name="tag_taxonomy_namex", columns={"name"})
 *    }
 * )
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class Taxonomy
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=50)
     */
    protected $name;
    /**
     * @ORM\OneToMany(targetEntity="Tag", mappedBy="taxonomy", fetch="LAZY")
     */
    protected $tags;
    // .....
}
Run Code Online (Sandbox Code Playgroud)

标签表:

<?php
/**
 * Tag
 *
 * @ORM\Table(
 *     name="tag",
 *    indexes={
 *        @ORM\Index(name="namex", columns={"name"})
 *    }
 * )
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class Tag
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue
     */
    protected $id;
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=50)
     */
    protected $name;
    /**
     * @ORM\ManyToOne(targetEntity="Taxonomy", inversedBy="tags", fetch="LAZY")
     * @ORM\JoinColumn(name="taxonomy_id", referencedColumnName="id", onDelete="SET NULL")
     */
    protected $taxonomy;
    // ....
}
Run Code Online (Sandbox Code Playgroud)

关系表:

<?php
/**
 * @ORM\Table(
 *     name="tag_tagging",
 *     uniqueConstraints={
 *        @ORM\UniqueConstraint(name="tagging_idx", columns={"tag_id", "entity_name", "record_id"})
 *    },
 *    indexes={
 *        @ORM\Index(name="entity_name_idx", columns={"entity_name", "record_id"})
 *    }
 * )
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity
 */
class Tagging
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
    /**
     * @ORM\ManyToOne(targetEntity="Tag", inversedBy="tagging", cascade="ALL")
     * @ORM\JoinColumn(name="tag_id", referencedColumnName="id", onDelete="CASCADE")
     **/
    protected $tag;
    /**
     * @var string
     * @ORM\Column(name="entity_name", type="string", length=100)
     */
    protected $entityName;
    /**
     * @var int
     * @ORM\Column(name="record_id", type="integer")
     */
    protected $recordId;
    // .....
}
Run Code Online (Sandbox Code Playgroud)

所以我已经有 3 列作为键的关系表:“tag_id”、“entity_name”、“record_id”。现在知道如何与任何实体创建关系以标记表吗?也许根本不需要标记实体?

我想知道为任何实体类型创建标签实体的最佳方法是什么。所以我可以为用户、文章或任何东西使用标签。当然,可以有许多关系为每个实体创建单独的关系表,对吗?但方法正确吗?

Ala*_* T. 5

即使它不允许在任何实体和Tagging类之间创建真正的关联(具有基础外键),您的方法也完全正确。不过,这完全可以接受。您需要一种方法来定义将进入您的entityName领域的内容,以便它可以有效地充当鉴别器领域。

正如您所说,您还可以为每个可以标记的实体创建一个单独的连接表。这是我目前在我的一个项目中使用的解决方案,它也运行良好。最后,这取决于您希望能够轻松执行的操作。

Tagging表/实体

您选择的这种方法也是FPNTagBundle中介绍的方法,它集成了自己的DoctrineExtensions-Taggable库。它依赖于一个Tagging实体,该实体是ManyToOne与您的Tag表的关联的拥有方,并拥有两个字段 (resourceIdresourceType) 来引用标记的实体。

如果您想沿着这条路走下去,我建议您查看 FPNTagBundle 的 Taggable 原则扩展实现。或者,您可以查看其他可标记的扩展名。它非常相似,但它以不同的方式使用学说事件来处理保存/加载。

优点:

  • Tag 可以在它与 Tagging
  • 轻松检索给定标签的标签实体的 ID,与它们的类型无关
  • 即使它们的类型不同,也可以轻松获取给定标签的标记实体的计数(您甚至可以通过简单的 group by 获得按类型计数)

请参阅两个扩展建议的存储库:herehere

缺点:

  • 您的实体和TagTagging
  • Tagging和您的实体之间的关系没有外键,因此您的应用程序必须处理孤立Tagging条目的删除(无法依赖于ON DELETE CASCADE
  • 检索实体的标签需要额外的查询或自定义连接,tags如果您希望在实体中使用其结果,则必须手动处理其结果才能正确地填充您的字段。

一个ManyToMany由每个加标签实体所有的关联

使用这种方法,您最终会为每个声明ManyToMany与您的Tag实体的关联的实体创建一个连接表。为了快速向实体添加标签,我个人使用TaggableTrait这样的标签 :

trait TaggableTrait
{    
    /** @ORM\ManyToMany(targetEntity="AppBundle\Entity\Tag\Tag") */
    private $tags;

    /* ... getter and add/remove methods ... */
}
Run Code Online (Sandbox Code Playgroud)

这样,通过use TaggableTrait我的实体中的简单语句,我可以启用标记。

优点:

  • 易于设置
  • 您可以通过简单的连接直接检索实体的标签
  • 您可以使用指定的附加条件直接按标签过滤实体WITH
  • 由于连接表的外键,您可以依靠您的数据库来删除孤立标记关联

缺点:

  • 如果你想Tag在反面,你需要ManyToMany为每个 Taggable 实体添加关联的反面
  • 您不能简单地检索给定标记的所有标记实体。对于给定的类型,您只能以简单的方式进行
  • 一般来说,对所有可标记实体执行操作会很麻烦(例如,我在我的项目中实现了标签的合并,因此我需要更新所有连接表。为此,我必须直接使用 classmetadata 并执行本机查询在每个连接表上执行我的更新)

无论如何,正如我在开始时所说的,这一切都归结为您想对标签执行什么样的操作。与两个字段resourceId和实体resourceType内部的通用关系Tagging可能会给您的应用程序内部带来更多的灵活性,但在处理某些事情的原则时,使用它也会有点棘手。