【问题标题】:Doctrine Single Table Inheritance preserving findAllDoctrine 单表继承保留 findAll
【发布时间】:2016-12-27 08:42:53
【问题描述】:

我在尝试使用 Doctrine Single Table Inheritance 时遇到了一个严重的问题。

我有一个Division 实体,它代表地理区域(城市、县、国家、地区或任何特定国家/地区的行政区域)。对于我的应用程序需求,我需要使用City 实体来专门识别城市。城市是世界上广泛共享的行政区域,我认为这是有道理的。

这是我基本上想要的:

class Division {
    /** @var boolean */
    protected $city;
}

class City extends Division {
    /** @var boolean */
    protected $city = true;
}

都是共享同一张表的 Doctrine 实体。

使用存储库,我希望能够获得这种行为:

// return counties, regions, AND cities but all mapped to Division class
$divisionsRepositories->findAll();

// return only cities, mapped to City class
$citiesRepositories->findAll();

// if id #12 is a division with $city = false, returns null
$citiesRepositories->find(12);

// persist to divisions table a row with city = 1
$citiesRepositories->persist(new City("Paris"));

我在鉴别器上玩了很多次,但找不到获得此结果的方法,因为鉴别器迫使我为 Division 实体做出独特的选择(因此查询总是得到 city IN ('0'))。我什至尝试创建一个“loadClassMetadata”事件侦听器来覆盖元数据,但它破坏了很多东西。

如果问“为什么”这个问题:类型提示、更准确的相关实体关系、特定存储库(以及更好的 DI)...很多原因!

关于如何用 Doctrine 重现这个的任何想法?看起来很简单,让我抓狂!

[编辑] 这是我在 cmets 中尝试过的 discriminatorMap (其中之一):

/**
 * @ORM\Table(name="divisions")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\Entity()
 * @ORM\DiscriminatorColumn(name="city", type="integer")
 * @ORM\DiscriminatorMap({
 *     0 = "Division",
 *     1 = "Division",
 *     1 = "City"
 * })
 */
abstract class AbstractDivision

【问题讨论】:

  • 你的鉴别器地图怎么样?你能告诉我们吗?
  • @ViníciusFagundes:使用示例编辑帖子
  • 我正在写一个答案,但是.. 如果City 本身始终是Division,为什么您需要将所有内容都返回为Division 实例?
  • 是的,我真的不想在 Division 结果中间有一个 City 对象,因为 City 本身就是一个部门

标签: php symfony doctrine-orm doctrine


【解决方案1】:

你不能那样做。我们在doctrine documentation中有@DiscriminatorMap的解释:

DiscriminatorMap 的键是数据库值,值是类。

因此,如果您将两个 Classes 设置为数据库值,则学说将如何返回将列 city=1 列为 City 或 Division 的行?

教义如何运作:

/**
 * @ORM\Table(name="divisions")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\Entity()
 * @ORM\DiscriminatorColumn(name="city", type="integer")
 * @ORM\DiscriminatorMap({
 *     0 = "Division",
 *     1 = "City"
 * })
 */
abstract class Division

// return Division and it subclasses but all mapped to their repectives classes
$divisionsRepositories->findAll();

// return only cities, mapped to City class
$citiesRepositories->findAll();

// if id #12 is a Division instance (city = false), returns null
$citiesRepositories->find(12);

// persist to divisions table a row with city = 1
$citiesRepositories->persist(new City("Paris"));

所以几乎每件事都像您预期的那样工作。第一种情况,$divisionsRepositories->findAll() 这是这里唯一的问题。但仔细看是不是因为:

//if $divisions returns for instance an array [City, Division]
$divisions = $divisionsRepositories->findAll();

// City instance
$city =  $division[0];

// Division instance
$division = $division[1];

echo $city instanceof City; // true, outputs 1
echo $city instanceof Division; // true, outputs also 1

echo $division instanceof City; // false, outputs empty
echo $division instanceof Division; // true, outputs 1

编辑

我发现关于你的问题和我的工作的唯一区别是:我没有使用抽象类。因此, Division 将具有所有注释,并且 AbstractdDivision 将被删除。

关于在查询生成器中按实例过滤,仅返回城市或分区。您应该像下面这样添加“实例”:

 //$qb as a QueryBuilder instance, to returns only instances of City
 $qb->from('\ApiBundle\Entity\Geography\Division','d');
 $qb->andWhere('d INSTANCE OF \ApiBundle\Entity\Geography\City');

【讨论】:

  • 老实说,findAll 方法就是一个例子。我主要关心的是整个 ORM 的使用:例如,任何由 Division 存储库中的 createQueryBuilder 方法构建的 QueryBuilder 都有 city IN ('0') where 条件自动添加,所以它“就这样”非常不可用。任何添加->where('d.city = 0') 的尝试都将导致Class ApiBundle\\Entity\\Geography\\Division has no field or association named city,因为该字段并未真正映射。
  • 我认为“忘记鉴别器并创建实体存储库的扩展版本”可能是最好的选择。在我的情况下,这也意味着创建我自己的工厂方法来创建存储库,因为我正在使用 Symfony 并将我的存储库声明为服务!
  • 我明白了。这是因为 city 不是一个映射列,一旦它是一个鉴别器,你就不能这样做,它就像一个元数据。
  • + 你错了,findAll() 只返回Division objects -_-'
  • 我已经更新了答案。可能是您只获得 Division 对象的原因。
【解决方案2】:

您需要为子实体定义至少一个repository 并覆盖findAll 和find 方法以实现您上面描述的。

【讨论】:

  • 这是我(最后)的想法,但我认为我需要覆盖 createQueryBuilder、find、findBy、findAll、...甚至 __call !老实说,这是我尽量避免的,这不值得努力......
  • 根据您在问题中提供的要求,我只能看到需要覆盖城市回购的 find 和 findAll。但是当然,如​​果 findBy 是您需要的另一个提取器,那么是的,您也需要涵盖它。我不确定您为什么需要操作 __call 和 createQueryBuilder 但听起来您的方法确实不正确。有一个实体覆盖一个表并有一个标志来识别它的性质是没有错的:)
  • 当然,我同意你的说法,但问题是,只要它允许为“主要”实体(又名“请给我整张桌子的教义,忘记对这个实体的歧视”;))。
  • @Ali,我认为没有必要这样做。只需像我在回答中所做的那样更新您的 DiscriminatorMap,一切都会按预期进行。
  • @ViníciusFagundes,喜欢这种方法,但在给定示例中将实体一分为二的附加值仍然不是 %100。我确信 Philippe 有一些充分的理由(可以在使用点提供类型提示),否则我仍然会投票支持简单性:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-29
相关资源
最近更新 更多