在 Doctrine 2 继承中覆盖 inversedBy 映射

Posted

技术标签:

【中文标题】在 Doctrine 2 继承中覆盖 inversedBy 映射【英文标题】:Overriding inversedBy mapping in Doctrine 2 inheritance 【发布时间】:2014-03-30 15:11:29 【问题描述】:

我有以下实体:

class Restaurant

    /**
     * @OneToMany(targetEntity="CollectionTime", mappedBy="restaurant")
     */
    protected $collectionTimes;

    /**
     * @OneToMany(targetEntity="DeliveryTime", mappedBy="restaurant")
     */
    protected $deliveryTimes;

映射到同一实体的两个子类:

/**
 * @Entity
 * @InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorMap(
 *   "CollectionTime" = "CollectionTime",
 *   "DeliveryTime"   = "DeliveryTime"
 * )
 */
abstract class OrderTime

    /**
     * @ManyToOne(targetEntity="Restaurant")
     */
    protected $restaurant;


/**
 * @Entity
 */
class CollectionTime extends OrderTime



/**
 * @Entity
 */
class DeliveryTime extends OrderTime


现在的问题是,doctrine orm:validate-schema 报如下错误:

Restaurant#collectionTimes 字段位于双向关系的反面,但目标实体 CollectionTime#restaurant 上指定的 mappedBy 关联不包含所需的 'inversedBy=collectionTimes' 属性。

Restaurant#deliveryTimes 字段位于双向关系的反面,但目标实体 DeliveryTime#restaurant 上指定的 mappedBy 关联不包含所需的“inversedBy=deliveryTimes”属性。

简而言之,Doctrine 期望每个mappedBy 在另一边都有一个inversedBy

目前我能看到的唯一解决方案是将OrderTime::$restaurant 属性和映射移动到CollectionTimeDeliveryTime,以便能够添加正确的inversedBy 映射:

abstract class OrderTime



/**
 * @Entity
 */
class CollectionTime extends OrderTime

    /**
     * @ManyToOne(targetEntity="Restaurant", inversedBy="collectionTimes")
     */
    protected $restaurant;


/**
 * @Entity
 */
class DeliveryTime extends OrderTime

    /**
     * @ManyToOne(targetEntity="Restaurant", inversedBy="deliveryTimes")
     */
    protected $restaurant;

但是比较繁琐,不符合继承的原则。

有没有办法只覆盖子类中的inversedBy 属性,而不必(重新)声明子类中的整个属性?

我研究了@AssociationOverrides 和@AttributeOverrides,但它们似乎不是为此目的而设计的。

【问题讨论】:

【参考方案1】:

从 Doctrine 2.6 开始,您可以覆盖 inversedBy。看起来像这样:

/**
 * @Entity
 * @ORM\AssociationOverrides(
 *     @ORM\AssociationOverride(name="restaurant", inversedBy="collectionTimes")
 * )
 */
class CollectionTime extends OrderTime



/**
 * @Entity
 * @ORM\AssociationOverrides(
 *     @ORM\AssociationOverride(name="restaurant", inversedBy="deliveryTimes")
 * )
 */
class DeliveryTime extends OrderTime


【讨论】:

【参考方案2】:

单向/双向

Doctrine 2 使用单向关联和双向关联的概念。

在定义单向关联时,您必须省略 inversedBy 参数,因为关联不会反转。

在定义双向关联时,您必须在拥有方使用inversedBy 参数(该方实际上包含所有元数据,以便 Doctrine 正确映射关联),并且您必须在反面上使用mappedBy 参数(这样Doctrine 就知道在哪里寻找关联的实际映射元数据)。

所以您要么同时使用inversedBy mappedBy(双向),要么使用它们(单向)。

继承原理

但是比较繁琐,不符合继承原则。

我认为这取决于你如何看待它:

如果您只看代码(而不是映射),那么您是正确的。 OrderTime 的两个具体实现共享一个属性 $restaurant(可能还有 getter、setter 和其他逻辑),因此原则要求您在 OrderTime 中定义它。

但是当您查看映射时,您有 2 个不同的关联:一个将 Restaurant::$collectionTimesCollectionTime::$restaurant 联系在一起,一个将 Restaurant::$deliveryTimesDeliveryTime::$restaurant 联系在一起。

因为这是 2 个不同的关联,所以 Doctrine 希望您正确定义它们是公平的。

您仍然可以通过以下方式坚持继承原则:在OrderTime中定义您需要的所有共享逻辑,甚至是属性$restaurant,只是不要添加映射元数据。在具体实现中,您可以使用适当的映射元数据重新声明属性$restaurant

您必须在这些具体情况中重新声明属性$restaurant 的唯一原因是您使用注释来映射元数据。使用 Yaml 或 XML 时,您不必重新声明属性,因为映射元数据将位于单独的文件中。

实际上,您在这些具体实现中定义的不是代码/逻辑,而是映射元数据。

值对象

那些OrderTime 类在我看来更像Value Objects(不是实体):A small simple object, like money or a date range, whose equality isn't based on identity.

Doctrine 将从 2.5 版 (see here) 开始支持值对象。

【讨论】:

感谢您的回答。我奖励赏金是为了避免任何机会失去它,即使我有几个 cmets ;-) 对我来说,在考虑这是否违反继承原则时不应该考虑映射,所以实际上只有一种方式来看待它。关于价值对象,我不认为它们是 TBH,但如果你能用一些示例代码证明我错了,我会很高兴!无论如何,您提到的功能似乎只允许映射单个 VO,而不是 VO 的集合,所以我认为现在几乎无关紧要。 关于继承:教义有点强迫你进入这个设置(但仅在使用注释时)。它还需要一个具有 2 个固定端点的关联,因此您不能让一侧有时指向实体 A,有时指向实体 B :( 谢谢,总结一下你的回答,我可以换成另一个映射驱动程序(XML 或 YAML),或者在父类中声明非映射属性,并在两个子类中重新声明属性(到目前为止我首选的方法)。我也可能会打开一个功能请求,以便能够覆盖子类中的inversedBy 参数;如果您是对的,并且可以使用其他映射驱动程序执行我想要的操作,那么注释驱动程序在逻辑上也应该是可行的! 没关系,this is possible 从 Doctrine 2.6 开始。

以上是关于在 Doctrine 2 继承中覆盖 inversedBy 映射的主要内容,如果未能解决你的问题,请参考以下文章

doctrine2 映射覆盖从 MappedSuperclass 继承的 inversedBy 字段。

Doctrine 1.2 hasOne 覆盖 hasMany 关系

Doctrine2 - 继承映射,查询子类

Doctrine 2 多重映射继承

PHP Doctrine 使用鉴别器映射查询继承的类

在 Doctrine 中使用继承时查询派生类型的属性