【问题标题】:Doctrine isDeletable method in entity实体中的学说 isDeletable 方法
【发布时间】:2015-08-31 18:22:07
【问题描述】:

我的数据库中有一个实体(比如成员),它与其他表有很多关系(准确地说是 6 个关系)。其中一些我不想用 ORM 映射(我的意思是链接到这个实体),因为它们可能有很多记录(例如 MemberAccessLogs),而其他一些则加载了许多其他实体。

现在我希望这个成员实体有一个 isDeletable 方法,这样我就可以禁用管理页面中的排除按钮。

如果我以传统方式执行此操作,我将必须声明与实体类中所有其他表的关联,包括 MemberAccessLogs,并且我会将方法放入其中,以便测试这些关联是否为空。

但是 AFAIU,我必须对关联的表进行提取(或至少计数)以检查是否为空。

另一种方法是获取我想要显示的成员,然后进行单独的查询以检查这些子表中是否存在低成本的空(从表限制 1 中选择 *),然后在成员中填充 isDeletable 方法在将其传递给 Twig 之前以编程方式进行。

但我发现这个解决方案很麻烦。有人有更好的方法吗?

仅作记录:有些人可能认为这是“过早的优化”。我坚持(与某些人相反),您在编程时应该提前考虑,这不是坏事。但我真的认为这里不是讨论它的地方。请让我们专注于问的问题好吗? :)

编辑

为了轻松证明 limit 1 比 count 快得多,我在我的数据库中的一个表中做了一个小测试,该表有超过 2000 万行。结果如下:

select count(*) from loga [20 million+ table]
20678473
1 row(s) fetched - 27023ms

select exists(select null from loga limit 1) 
true
1 row(s) fetched - 2ms

我猜快 13511,5 倍就足够了。 :D

【问题讨论】:

  • 我看不出select * from table limit 1count(*) from table 更有效。
  • 包含在这个问题上的编辑。
  • 如果你愿意贡献(即使没有),请考虑创建一个 PR 让 Doctrine\ORM\PersistentCollection::isEmpty() 按照你的描述去做。看起来这可能是一个相当不错的优化。您的问题的解决方案也会变得更容易;)
  • 好的。我在。问题是时间。但是我会在星期二回去工作时检查一下。 :)

标签: php symfony doctrine-orm twig


【解决方案1】:

特别懒

您可以查看extra-lazy associations

基本上你像往常一样映射所有关联,并添加fetch="EXTRA_LAZY"

/**
 * @Entity
 */
class CmsGroup
{
    /**
     * @ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
     */
    public $users;
}

现在 Doctrine 不会在第一次访问时将 complete 集合加载到内存中,而是执行专门的查询来加载您实际使用的 parts那个时候需要。

所以集合上的$users->count()(或count($users))会触发一个简单的计数查询,而不是将整个集合加载到内存中。

后加载

您可以使用postLoad 事件来确定此类实体是否可删除。这个postLoad事件是在EntityManager构造一个实体之后调用的,所以在实体加载的时候。

为实体添加一个未映射的属性($isDeletable),用于存储该实体是否可以删除。

创建一个监听postLoad 事件的entity listener。侦听器可以注入 EntityManager、DBAL 连接或其他任何东西。有了这个依赖,你可以执行任何你想要的查询并使用结果来设置$isDeletable

结果是加载实体时的单个附加查询,之后实体“知道”它是否可删除。

使用postLoad 事件的示例可以在Cookbook entry on the Strategy Pattern 中找到

请注意,当确定它是否可删除的条件发生变化时,$isDeletable 的值可能会变得不正确。要解决此问题,您可以跟踪这些情况:

跟踪

给实体添加一个mapped属性($isDeletable),存储实体是否可以删除。它可能以true 开头。

当向关联添加某些内容时,这意味着该实体不再可删除,请将 $isDeletable 设置为 false

如果从关联中删除某些内容,这意味着该实体可以再次删除,请将 $isDeletable 设置为 true

换句话说:每次更改时,您都会跟踪实体是否可删除。

这样您根本不需要任何额外的查询。

有一个Cookbook entry on aggregate fields很好地解释了这个概念。

【讨论】:

  • 你说的超懒关联仍然使用计数。在我看来,这是错误的做法。在一个大表中,计数仍然非常昂贵。正如我在 Doctrine 的邮件列表中曾经说过的那样,如果使用 exists(select null from subtable limit 1) 来完成会好得多。 “limit 1”在每个支持它的数据库中都是瞬时的。所以这就是为什么我不喜欢使用额外的惰性关联。
  • 保持跟踪会增加复杂性,我认为它不适合 POPO。但是我不知道 OnLoad 的事情。您的意思是我可以将 DQL 或 Native SQL 放在实体本身上吗?我找不到任何有关它的文档或示例。你能指点我吗?
  • 我已经更新了我的答案,“onLoad”部分现在更深入了。另外,该事件实际上是 postLoad,对此感到抱歉!
  • Offtopic:我不同意将实体视为 POPO。我强烈建议您观看 Mathias Verraes 的演讲 Unbreakable domain models。恕我直言,实体是放置“跟踪”逻辑的唯一正确的位置
  • 你的方法很有趣。我一回到工作岗位就测试一下。我也会检查谈话内容。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2018-08-08
  • 2012-02-15
相关资源
最近更新 更多