实体公共字段的学说继承

时间:2016-11-28 15:05:37

标签: php inheritance doctrine-orm zend-framework3 zf3

我在我的网站项目中使用Zend Framework 3和Doctrine ORM。

我的应用程序中有几个模块(UserStockSales)以及每个模块上的一些实体模型:

  • User模块实体:UserAccount等。
  • Stock模块实体:SKUStockLevel等。
  • Sales模块实体:InvoicePaymentMethod等。

默认情况下,所有实体都有公共字段,例如:

  • creationDateTime:创建日期/时间
  • creationUser:创建实体的用户
  • lastChangeDateTime:上次实体更改的日期/时间
  • lastChangeUser:上次更改实体的用户

我不想放置那些字段或每个实体,而是创建一个扩展我所有实体的项目基类。我需要有适用于所有实体的通用方法,即:

  /**
   * Update the last change fields
   * @param string $user User that is updating 
   */
  public void updateLastChange($user)
  {
      $this->lastChageDataTime = \Datetime();
      $this->lastChangeUser = $user;
  }

正如我所看到的from the documentation,我认为我需要使用单表继承,但我无法弄清楚具体如何。问题:

a)通过使用单表继承,Doctrine会在数据库中为这些字段创建基表,还是会为每个实体表加入基表和实体字段,换句话说,我会只有实体表或这个继承还会为基本字段创建一个数据库表吗?

b)我应该在哪里放置我的基本实体,以便可以为不同模块上的所有实体继承它?

我很感激有人可以提供一些关于如何做的示例/链接。

1 个答案:

答案 0 :(得分:7)

对于你想要做的事情,单表继承不是你需要的。

有两个选项:

1)MappedSuperClass(几乎直接来自文档)

您制作了MappedSuperClass(文档可以在 6.1: Mapped Superclasses 一章中找到),并在该基类中添加这些常用字段。然后,从基础(映射超类)类扩展所有需要这些字段的类。

/** 
 * @MappedSuperclass 
 */
class MappedSuperclassBase
{
    /** @Column(type="datetime") */
    protected $creationDateTime;

    /** 
     * @ManyToOne(targetEntity="Application\Entity\User") 
     * @JoinColumn(name="created_by", referencedColumnName="id")
     */
    protected $creationUser;

    /** @Column(type="datetime") */
    protected $lastChangeDateTime;

    /** 
     * @ManyToOne(targetEntity="Application\Entity\User") 
     * @JoinColumn(name="updated_by", referencedColumnName="id")
     */
    protected $lastChangeUser;

    // ... more fields and methods
}

/** 
 * @Entity 
 */
class EntitySubClass extends MappedSuperclassBase
{
    /** @Id @Column(type="integer") */
    private $id;

    // ... more fields and methods
}

2)你使用Trait

您在需要拥有这些公共字段的所有类中使用特征(或每个字段/关联的几个单独特征)。

trait BaseTrait
{
    /** @Column(type="datetime") */
    protected $creationDateTime;

    /** 
     * @ManyToOne(targetEntity="Application\Entity\User") 
     * @JoinColumn(name="created_by", referencedColumnName="id")
     */
    protected $creationUser;

    /** @Column(type="datetime") */
    protected $lastChangeDateTime;

    /** 
     * @ManyToOne(targetEntity="Application\Entity\User") 
     * @JoinColumn(name="updated_by", referencedColumnName="id")
     */
    protected $lastChangeUser ;

    // ... more fields and methods
}

/** 
 * @Entity 
 */
class EntitySubClass
{
    use BaseTrait;

    /** @Id @Column(type="integer") */
    private $id;

    // ... more fields and methods
}

您的问题的答案:

a)在文档中,您可以阅读:

  

单表继承是一种继承映射策略,其中层次结构的所有类都映射到单个数据库表。为了区分哪一行代表层次结构中的哪种类型,使用了所谓的鉴别器列。

这意味着所有这些实体都会共享一个共同的表,这绝对不是你想要的。它可能会成为一个巨大的表(每个实体的一行),减慢您的查询速度。除此之外,表格中还会有所有不常用的共享字段的列,而对于没有这些字段的实体,这些列将为空(null)。这也意味着那些非共享字段不能具有null约束。再次直接来自文档:

  

对于单表继承在您使用旧数据库模式或自编写数据库模式的情况下工作,您必须确保所有列不在根实体中但在任何不同的列中子实体必须允许空值。具有NOT NULL约束的列必须位于单表继承层次结构的根实体上。

此类继承仅对类似于大范围的实体是必要的,并且不适合您在问题中谈论的示例。

b)您只需将基础实体(MappedSuperClass)添加到通用模型中(例如Application文件夹)。