覆盖Doctrine 2继承中的inversedBy映射

问题描述 投票:7回答:2

我有以下实体:

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,但它们似乎并不是为此而设计的。

doctrine-orm
2个回答
7
投票

Unidirectional / bidirectional

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

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

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

所以你要么使用inversedBymappedBy(双向),要么根本不使用它们(单向)。

Principle of inheritance

但它很麻烦,违背了继承原则。

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

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

但是当你看到映射时,你有两种不同的联想:一种将Restaurant::$collectionTimesCollectionTime::$restaurant联系在一起,另一种将Restaurant::$deliveryTimesDeliveryTime::$restaurant联系在一起。

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

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

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

所以实际上它不是你在那些具体实现中定义的代码/逻辑,而是映射元数据。

Value Object

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

Doctrine将支持从2.5版开始的Value Objects(see here)。


3
投票

你可以在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
{
}
© www.soinside.com 2019 - 2024. All rights reserved.