ino
ino

Reputation: 2611

Symfony4 search within a PersistentCollection

I am strugling with my Symfony PersistentCollection - in a result set of query findByOne() executed on a repository. I need to find one or more elements by specific value within a PersistentCollection, ie withing other entity that has relation to the other.

I have a RuleSet entity with relation to SrcFile entity. On a RuleSetRepository I call method findOneBy(['id' => 1) to find RuleSet ID=1. This returns an object RuleSet that has srcFiles: PersistentCollection.

Now what I need is to get specific object from that collection. I know there is a method getIterator() I can loop through all the result in the collection and make condition to find wher file.kind == 'master', but...

Is there any better way how to access/find/search within the PersistentCollection using buildin method?

//...
// dump of acctual result of 
dump($grsr->findOneBy(['id' => 1)]);

// dumped data (shortened version):
ProfileUController.php on line 118:
RuleSet {#1509 ▼
  -id: 1
  -srcFiles: PersistentCollection {#1507 ▼
    -snapshot: array:5 [ …5]
    -owner: RuleSet {#1509}
    -association: array:15 [ …15]
    -em: EntityManager {#975 …11}
    -backRefFieldName: "ruleset"
    -typeClass: ClassMetadata {#1230 …}
    -isDirty: false
    #collection: ArrayCollection {#1481 ▼
      -elements: array:5 [▼
        0 => SrcFile {#1573 ▶}
        1 => SrcFile {#1734 ▶}
        //...
      ]
    }
    #initialized: true
  }
}

Upvotes: 0

Views: 1610

Answers (1)

patrick3853
patrick3853

Reputation: 1130

What you are looking for is Doctrine Criteria, which allows you to filter a Collection. It is smart enough to filter at the database level if the collection objects aren't in memory yet.

Here's a quick example:

$ruleSet = $grsr->findOneBy(['id' => 1]);
$criteria = Criteria::create();
// Criteria to find SrcFiles where kind = 'master'
$criteria->where(Criteria::expr()->eq('kind', 'master');
// return only the srcFiles matching criteria
$ruleSet->srcFiles->matching($criteria);

I prefer to create helper methods on my entity, as I think it's a little cleaner. This is up to you, but I would do something like this on the RuleSet entity:

public function getMasterFiles()
{
    $criteria = Criteria::create();
    $criteria->where(Criteria::expr()->eq('kind', 'master'));
    // assuming you have a getter named getSrcFiles for the srcFiles association
    return $this->getSrcFiles()->matching($criteria);
}

Then you can do something like this (and you don't have to duplicate the code every time we need the master files):

$ruleSet = $grsr->findOneBy(['id' => 1]);
$ruleSet->getMasterFiles();

Update

If you need to filter on nested collections, you have to use the filter method:

public function getMasterFiles()
{
    return $this->getSrcFiles()->filter(function(SrcFile $srcFile) {
        // if callback returns true, then object is included in result
        return ($srcFile->getKind() === 'master' && $srcFile->getSrcSheet()->getName() === 'MainData');
    });
}

The filter method is more flexible, but it is always done on the objects after they are fetched from the database. This usually isn't a big deal, but can affect performance. The other option is a custom repository method, but I prefer to use helper methods on the entities. It feels more ORMish to me.

Upvotes: 2

Related Questions