与doctrine创建一对多的多态关系

Roe*_*and 16 php mysql orm doctrine doctrine-orm

首先让我概述一下这个场景.我有一个Note对象,可以分配给许多不同的对象

  • 本书可以有一个或多个音符.
  • 一个图片可以有一个或多个注释秒.
  • 一个地址可以有一个或多个注释秒.

我想象数据库看起来像:

id | title           | pages
1  | harry potter    | 200
2  | game of thrones | 500
Run Code Online (Sandbox Code Playgroud)

图片

id | full      | thumb
2  | image.jpg | image-thumb.jpg
Run Code Online (Sandbox Code Playgroud)

地址

id | street      | city
1  | 123 street  | denver
2  | central ave | tampa
Run Code Online (Sandbox Code Playgroud)

注意

id | object_id | object_type | content      | date
1  | 1         | image       | "lovely pic" | 2015-02-10
2  | 1         | image       | "red tint"   | 2015-02-30
3  | 1         | address     | "invalid"    | 2015-01-05
4  | 2         | book        | "boobies"    | 2014-09-06
5  | 1         | book        | "prettygood" | 2016-05-05
Run Code Online (Sandbox Code Playgroud)

问题是,我如何在Doctrine中对此进行建模.我读到的关于单表继承的讨论,没有一对多的关系.

要注意(没有双关语;),您可能会注意到音符永远不需要基于与其相关的对象的唯一属性.

理想情况下,我可以做$address->getNotes()返回相同类型的Note对象$image->getNotes().

至少我要解决的问题是避免使用三个不同的表:image_notes,book_notes和address_notes.

Dav*_*san 5

这个问题给应用程序带来了不必要的复杂性。仅仅因为音符具有相同的结构并不意味着它们是相同的实体。在 3NF 中对数据库建模时,它们不是同一个实体,因为注释不能从 Book 移动到 Address。在您的描述中,book 和 book_note 等之间存在明确的父子关系,因此对其进行建模。

更多的表对数据库来说不是问题,但不必要的代码复杂性是,正如这个问题所展示的那样。这只是为了聪明而聪明。这是 ORM 的问题,人们停止进行完全规范化并且没有正确建模数据库。


Ste*_*ers 4

就我个人而言,我不会在这里使用超类。认为有更多的情况可以通过,和实现接口BookImageAddress

interface iNotable
{
    public function getNotes();
}
Run Code Online (Sandbox Code Playgroud)

这里以书为例:

class Book implements iNotable {
    /**
     * @OneToMany(targetEntity="Note", mappedBy="book")
     **/
    protected $notes;

    // ...        

    public function getNotes()
    {
        return $this->notes;
    }
}
Run Code Online (Sandbox Code Playgroud)

Note然后需要@ManyToOne相反的关系,只有其中一个适用于每个实体实例:

class Note {
    /**
     * @ManyToOne(targetEntity="Book", inversedBy="notes")
     * @JoinColumn
     **/
    protected $book;

    /**
     * @ManyToOne(targetEntity="Image", inversedBy="notes")
     * @JoinColumn
     **/
    protected $image;

    /**
     * @ManyToOne(targetEntity="Address", inversedBy="notes")
     * @JoinColumn
     **/
    protected $address;

    // ...
}
Run Code Online (Sandbox Code Playgroud)

如果您不喜欢此处对每个值得注意的类的多个引用,您可以使用继承并拥有类似AbstractNotable使用单表继承的超类之类的东西。虽然这现在看起来更整洁,但我不推荐它的原因是,随着数据模型的增长,您可能需要引入类似的模式,最终可能使继承树变得难以管理。

编辑:让验证器通过检查为每个实例设置的,或Note之一来确保数据完整性也很有用。像这样的东西:$book$image$address

/**
 * @PrePersist @PreUpdate
 */
public function validate()
{
    $refCount = is_null($book) ? 0 : 1;
    $refCount += is_null($image) ? 0 : 1;
    $refCount += is_null($address) ? 0 : 1;

    if ($refCount != 1) {
        throw new ValidateException("Note references are not set correctly");
    }
}
Run Code Online (Sandbox Code Playgroud)