【问题标题】:Doctrine inheritance for entities common fields实体公共字段的学说继承
【发布时间】:2017-04-12 08:40:08
【问题描述】:

我正在为我的 Web 项目使用 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)我应该把我的基础实体放在哪里,以便它可以被不同模块上的所有实体继承?

如果有人可以提供一些示例/链接,我将不胜感激。

【问题讨论】:

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


    【解决方案1】:

    因为你想做的单表继承不是你需要的。

    有两种选择:

    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 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)您可以在通用模型(例如您的Application 文件夹)中添加基本实体(即MappedSuperClass)。

    【讨论】:

    • 我会推荐 trait 方法。然后,您可以实现通用侦听器,它将自动设置这些字段。看看 - github.com/KnpLabs/DoctrineBehaviors - 他们为你做了大部分工作 J
    • trait 运行良好,包括orm:generate:entities。非常感谢。 Jan,订阅者的东西是一个加号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-15
    • 1970-01-01
    • 1970-01-01
    • 2013-02-04
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    相关资源
    最近更新 更多