Doctrine 2的ArrayCollection过滤方法

74

我能在使用Doctrine 2的延迟加载时过滤掉arrayCollection中的结果吗?例如:

// users = ArrayCollection with User entities containing an "active" property
$customer->users->filter('active' => TRUE)->first()

我不太清楚filter方法实际上是如何使用的。


但是仍然存在方法加载过多的数据,例如对于计数,会加载所有匹配的数据。 - user2797323
5个回答

138

Doctrine现在有Criteria,它提供了一个单一的API,可以根据上下文使用SQL和PHP来过滤集合。

https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html#filtering-collections

更新

使用这种方法可以在不从数据库获取所有内容的情况下实现接受答案中的结果。

use Doctrine\Common\Collections\Criteria;

/**
 * @ORM\Entity
 */
class Member {
  // ...
  public function getCommentsFiltered($ids) {
    $criteria = Criteria::create()->where(Criteria::expr()->in("id", $ids));

    return $this->getComments()->matching($criteria); 
  }
}

2
谢谢您的澄清,这实际上会更改SQL查询,而不是从数据库中选择所有内容,然后通过循环应用过滤器! - tftd
请看这里:https://dev59.com/6pTfa4cB1Zd3GeqPKgK9 - DonCallisto
1
在使用 indexBy="xxx" 对集合进行索引并调用 matching 时,似乎存在问题,其中索引会被丢弃。https://github.com/doctrine/doctrine2/issues/4693 - Will B.
1
请注意:只有在使用fetch="EXTRA_LAZY"声明关系时,Criteria才能像预期的那样工作(使用SQL而不是通过PHP进行过滤)。然而,并非每个关系都适用于fetch="EXTRA_LAZY"...http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html - V-Light

104

这篇文章中Boris Guéry的回答可能会对你有所帮助:Doctrine 2, query inside entities

$idsToFilter = array(1,2,3,4);

$member->getComments()->filter(
    function($entry) use ($idsToFilter) {
       return in_array($entry->getId(), $idsToFilter);
    }
); 

7
使用过滤方法的唯一问题是你必须在筛选数据之前获取所有数据,你知道是否有一种方式可以不获取全部数据而完成筛选吗? - Dennis
嗨 - 我正在尝试上述内容,但遇到语法错误,你能更新你的答案吗? - Sjwdavies
1
有一件事咬了我,那就是filter返回一个集合。我以为它返回一个数组,所以一直在使用$filtered[0],但得到的是null。当我将代码更改为$filtered->first()时,我得到了期望的数据。 - Dan Morphis
7
@ryan对Criteria的回答现在(2014年)应该被接受为正确答案,因为它可以在SQL层面过滤集合。 - jcbwlkr
3
@tftd 哦,当然。我的评论并不是表示不尊重。相反,我是想通知读者现在可用的新方法。 - jcbwlkr
显示剩余2条评论

16

您的使用情境将是:

    $ArrayCollectionOfActiveUsers = $customer->users->filter(function($user) {
                        return $user->getActive() === TRUE;
                    });

如果您添加 ->first(),您将仅获取返回的第一条记录,这不是您想要的。

@Sjwdavies 您需要在传递给 USE 的变量周围加上括号。您还可以缩短代码,因为 in_array 返回一个布尔值:

    $member->getComments()->filter( function($entry) use ($idsToFilter) {
        return in_array($entry->getId(), $idsToFilter);
    });

0
以下代码将解决您的需求:
//$customer = ArrayCollection of customers;
$customer->getUsers()->filter( 
    function (User $user) {
        return $user->getActive() === true;
    }
);

-1

Collection#filter 方法确实会预先加载所有成员。 在Doctrine 2.3中将添加在SQL级别进行过滤的功能。


1
现在2.3版本已经发布了,这是真的吗?我在文档中没有找到相关内容。 我们现在能否执行过滤等操作,期望集合将过滤应用于查询并推迟查询? - Ivan Pintar
@Pinetree 至少他们这样说:http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#filtering-collections - Robin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接