64

Can I filter out results from an arrayCollection in Doctrine 2 while using lazy loading? For example,

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

It's unclear for me how the filter method is actually used.

ЯegDwight
  • 23,615
  • 10
  • 43
  • 51
Dennis
  • 3,338
  • 8
  • 36
  • 45
  • But still method load to many data, for example for count, load all matching data. –  Sep 19 '13 at 23:05

4 Answers4

131

Doctrine now has Criteria which offers a single API for filtering collections with SQL and in PHP, depending on the context.

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

Update

This will achieve the result in the accepted answer, without getting everything from the database.

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); 
  }
}
cezar
  • 9,952
  • 5
  • 35
  • 74
Ryan
  • 3,607
  • 1
  • 23
  • 33
  • Thanks for clearing out this will actually alter the sql query, rather than selecting everything from the database and then applying a filter via a cycle! – tftd Nov 04 '14 at 03:33
  • http://stackoverflow.com/questions/35358597/in-predicate-with-criteria-filtering-isnt-working please, take a look here :) – DonCallisto Feb 12 '16 at 09:35
  • 1
    There appears to be an issue when using `indexBy="xxx"` on a collection and calling `matching` on it, where the indexes become discarded. https://github.com/doctrine/doctrine2/issues/4693 – Will B. Mar 16 '16 at 14:08
  • 1
    Please Note: `Criteria` works as assumed (SQL instead of filtering via PHP) only if relation declared with `fetch="EXTRA_LAZY"`. `fetch="EXTRA_LAZY"` doesn't work on every relation yet... http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html – V-Light Mar 30 '17 at 11:00
94

The Boris Guéry answer's at this post, may help you: Doctrine 2, query inside entities

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

$member->getComments()->filter(
    function($entry) use ($idsToFilter) {
       return in_array($entry->getId(), $idsToFilter);
    }
); 
Community
  • 1
  • 1
FredRoger
  • 1,437
  • 12
  • 17
  • 5
    The only problem using the filter method is that you have to fetch all the data before you can filter it out, do you know if there is a way of doing this without fetching everything? – Dennis Dec 01 '11 at 19:37
  • Hi - I'm trying the above but getting a syntax error, can you update your answer? – Sjwdavies May 05 '12 at 12:04
  • @Sjwdavies I updated it for you. Missing () around $idsToFilter. – Jrgns Sep 14 '12 at 03:08
  • 1
    Something that bit me is filter returns a Collection. I presumed it returned an array and kept doing $filtered[0], and got null. When I changed the code to $filtered->first() I got the data I expected. – Dan Morphis Jan 03 '14 at 19:58
  • 6
    @ryan's answer about `Criteria` should now (2014) be the accepted answer as it can filter collections at the SQL level. – jcbwlkr Jul 16 '14 at 21:13
  • @jacobwalker0814 although you are right, this is an old question and the answer back then was the correct one (though now it's obsolete). – tftd Nov 04 '14 at 13:59
  • 3
    @tftd oh, yeah for sure. My comment meant no disrespect. Instead I meant to notify readers of the newer method that is now available. – jcbwlkr Nov 04 '14 at 16:35
15

Your use case would be :

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

if you add ->first() you'll get only the first entry returned, which is not what you want.

@ Sjwdavies You need to put () around the variable you pass to USE. You can also shorten as in_array return's a boolean already:

    $member->getComments()->filter( function($entry) use ($idsToFilter) {
        return in_array($entry->getId(), $idsToFilter);
    });
Stéphan Champagne
  • 1,149
  • 11
  • 12
-1

The Collection#filter method really does eager load all members. Filtering at the SQL level will be added in doctrine 2.3.

ЯegDwight
  • 23,615
  • 10
  • 43
  • 51
Smoky McPot
  • 59
  • 1
  • 6
  • 1
    is this true now that 2.3 is out? I haven't found it in the docs. Can we now do stuff like filter and other things, expecting that the collection will apply the filtering to the query and defer the query? – Ivan Pintar Sep 21 '12 at 17:19
  • @Pinetree At least they say so: http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#filtering-collections – Robin Aug 09 '13 at 16:05