用原则2中的所有相关实体填充部分集合


Fill a partial collection with all related entities in Doctrine 2

假设我有以下查询,该查询返回带有已标记的关联Post实体的User实体(这只是为了演示):

SELECT u, p
FROM User u
LEFT JOIN u.posts p
WHERE p.isFlagged = true

一旦我使用了这个,然后我想要访问该用户的所有帖子,而不考虑isflag。是否有一种简单的方法来刷新$user->posts集合,使其成为所有用户帖子的完整集合?

我不想只是拉出查询中的所有帖子(因为它们可能不需要),并且需要完整集合的代码将不知道该实体来自哪个查询。

你在这里做的是坏的:

SELECT
    u, p
FROM
    User u
JOIN
    u.posts p
    WITH
    p.isFlagged = true

这将使"User#posts"集合具有错误的值,从而导致逻辑损坏,对象图损坏,所有内容都损坏。

您应该在DQL级别解决这个问题,如下所示:

SELECT
    u, p
FROM
    User u
JOIN
    u.posts p
JOIN
    u.posts j
    WITH
    j.isFlagged = true

这将在用户对象中生成正确的帖子集合,没有任何中间状态。

EDIT:我误解了这个问题,因为我的想法是基于@Athlan的答案,这从根本上是错误的(冲洗一个破碎的集合真的是错误的)。这是我之前的答案,我仍然认为它很有趣,因为它解决了问题,但不是以一种真正正确的方式。

这个问题实际上让我很好奇,想看看ORM是否像我期望的那样刷新集合。

我在这个分支中编写了测试。

基本上,你需要做的很简单:

$entityManager->refresh($entity);

下面是测试的相关部分:

$foo = new DDC2666Foo();
$this->_em->persist($foo);
$this->_em->flush();
$this->_em->clear();
$fetchedFoo = $this->_em->find(__NAMESPACE__ . ''DDC2666Foo', $foo->id);
$fetchedFoo->bars->add(new DDC2666Bar());
$this->assertCount(1, $fetchedFoo->bars);
$this->_em->refresh($fetchedFoo);
$this->assertCount(0, $fetchedFoo->bars, 'The collection was reset');

这样做的缺点是它也会重新获取您的实体,但是ORM本身不提供刷新单个集合的外观。

无论如何,这也是一件好事,因为这样,您就不会破坏封装,这可能会导致代码中出现意想不到的(并且难以调试)行为。

我也有同样的问题,最后在这里结束。刷新对我来说不是一个选项,因为我有一个列表,我不想做那些额外的查询。

这里真正需要的是添加一个"假"连接来进行过滤,而保留原始连接而不带条件…说明:

SELECT u, p
FROM User u
LEFT JOIN u.posts p
LEFT JOIN u.posts p2
WHERE p2.isFlagged = true

请注意,我在select子句中省略了p2,它只是用于过滤目的。

我发现其他解决方案非常不令人满意,因为许多实体会得到我根本不感兴趣的水分。如果绝大多数帖子都有isFlagged = 0,那么性能影响是巨大的。

这是另一种解决方案,涉及交换实体之间的关系。然而,并不完全等同于原问题所要求的。然而,它可能对其他有这个问题的人有用:

SELECT p, u
FROM Post p
JOIN p.user u
WHERE p.isFlagged = true

此解决方案与其他解决方案的区别在于,它将不再返回没有任何帖子或任何标记帖子的用户。这可能是你想要的,也可能不是。