与教义建立一对多多态关系

2022-08-30 20:07:18

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

  • 本书可以有一个或多个“笔记”。
  • 一个图像可以有一个或多个“注释”。
  • 一个地址可以有一个或多个注释

我设想数据库的样子是这样的:

id | title           | pages
1  | harry potter    | 200
2  | game of thrones | 500

图像

id | full      | thumb
2  | image.jpg | image-thumb.jpg

地址

id | street      | city
1  | 123 street  | denver
2  | central ave | tampa

注意

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

问题是,我如何在教义内部对此进行建模。我所读到的所有内容都涉及没有一对多关系的单表继承。

要注意(没有双关语的意思;),您可能会注意到,note 从不需要基于其相关对象的唯一属性。

理想情况下,我可以执行这将返回的相同类型的Note对象。$address->getNotes()$image->getNotes()

至少,我试图解决的问题是避免有三个不同的表:image_notes,book_notes和address_notes。


答案 1

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

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


答案 2

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

interface iNotable
{
    public function getNotes();
}

以下是书籍为例:

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

    // ...        

    public function getNotes()
    {
        return $this->notes;
    }
}

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;

    // ...
}

如果您不喜欢这里对每个值得注意的类的多个引用,则可以使用继承,并使用单个表继承来创建类似于超类的东西。虽然现在看起来可能更整洁,但我不建议这样做的原因是,随着数据模型的增长,您可能需要引入类似的模式,这些模式最终可能会使继承树变得难以管理。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");
    }
}

推荐