Doctrine 2 ArrayCollection filter method
Asked Answered
M

5

74

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.

Moulden answered 30/11, 2011 at 23:11 Comment(1)
But still method load to many data, for example for count, load all matching data.Cabe
B
105

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);
    }
); 
Baez answered 1/12, 2011 at 5:21 Comment(7)
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?Moulden
Hi - I'm trying the above but getting a syntax error, can you update your answer?Musician
@Musician I updated it for you. Missing () around $idsToFilter.Insomnolence
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.Farfetched
@ryan's answer about Criteria should now (2014) be the accepted answer as it can filter collections at the SQL level.Banky
@jacobwalker0814 although you are right, this is an old question and the answer back then was the correct one (though now it's obsolete).Revenue
@Revenue oh, yeah for sure. My comment meant no disrespect. Instead I meant to notify readers of the newer method that is now available.Banky
S
139

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); 
  }
}
Slowly answered 3/9, 2013 at 4:22 Comment(4)
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!Revenue
#35359097 please, take a look here :)Diegodiehard
There appears to be an issue when using indexBy="xxx" on a collection and calling matching on it, where the indexes become discarded. github.com/doctrine/doctrine2/issues/4693Piggery
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... docs.doctrine-project.org/projects/doctrine-orm/en/latest/…Extinction
B
105

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);
    }
); 
Baez answered 1/12, 2011 at 5:21 Comment(7)
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?Moulden
Hi - I'm trying the above but getting a syntax error, can you update your answer?Musician
@Musician I updated it for you. Missing () around $idsToFilter.Insomnolence
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.Farfetched
@ryan's answer about Criteria should now (2014) be the accepted answer as it can filter collections at the SQL level.Banky
@jacobwalker0814 although you are right, this is an old question and the answer back then was the correct one (though now it's obsolete).Revenue
@Revenue oh, yeah for sure. My comment meant no disrespect. Instead I meant to notify readers of the newer method that is now available.Banky
G
16

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);
    });
Gossett answered 10/5, 2012 at 14:22 Comment(0)
C
0

The following code will resolve your need:

//$customer = ArrayCollection of customers;
$customer->getUsers()->filter( 
    function (User $user) {
        return $user->getActive() === true;
    }
);
Coquelicot answered 2/8, 2022 at 8:47 Comment(0)
E
-1

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

Electrocardiograph answered 30/8, 2012 at 12:24 Comment(2)
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?Profuse
@Pinetree At least they say so: docs.doctrine-project.org/en/latest/reference/…Koslo

© 2022 - 2024 — McMap. All rights reserved.