Я борюсь со своим Symfony PersistentCollection - в наборе результатов запроса findByOne(), выполненного в репозитории. Мне нужно найти один или несколько элементов по определенному значению в PersistentCollection, то есть в другом объекте, который имеет отношение к другому.
У меня есть сущность RuleSet, связанная с сущностью SrcFile.
В RuleSetRepository я вызываю метод findOneBy(['id' => 1), чтобы найти RuleSet ID=1.
Это возвращает объект RuleSet, который имеет srcFiles: PersistentCollection.
Теперь мне нужно получить конкретный объект из этой коллекции. Я знаю, что есть метод getIterator() Я могу перебрать весь результат в коллекции и поставить условие, чтобы найти, где file.kind == 'master', но...
Есть ли лучший способ доступа/нахождения/поиска в PersistentCollection с использованием встроенного метода?
//...
// 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
}
}




То, что вы ищете, это Доктрина Criteria, который позволяет вам фильтровать Collection. Он достаточно умен, чтобы фильтровать на уровне базы данных, если объекты коллекции еще не находятся в памяти.
Вот краткий пример:
$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);
Я предпочитаю создавать вспомогательные методы для своей сущности, так как считаю, что это немного чище. Это зависит от вас, но я бы сделал что-то подобное с сущностью RuleSet:
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);
}
Затем вы можете сделать что-то вроде этого (и вам не нужно дублировать код каждый раз, когда нам нужны мастер-файлы):
$ruleSet = $grsr->findOneBy(['id' => 1]);
$ruleSet->getMasterFiles();
Обновлять
Если вам нужно отфильтровать вложенные коллекции, вы должны использовать метод filter:
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');
});
}
Метод filter более гибкий, но он всегда применяется к объектам после их извлечения из базы данных. Обычно это не имеет большого значения, но может повлиять на производительность. Другой вариант — пользовательский метод репозитория, но я предпочитаю использовать вспомогательные методы для сущностей. Мне он кажется более ORMish.
Идеально! Это то, что я ищу. Еще один вопрос - я не упомянул об этом в своем исходном сообщении, чтобы сделать его максимально простым, но srcFiles, соответствующие критериям, имеют свою собственную коллекцию с именем srcSheets. Как связать запрос, чтобы найти файл expr()->eq('kind', 'master') с srcSheet ...expr()->eq('name', 'MainData')?
Это становится более сложным. Criteria поддерживает только свойства непосредственно в ассоциации, поэтому вы не можете фильтровать вложенные ассоциации. Для этого нужно использовать функцию filter, которая отлично работает, но не будет фильтровать на уровне SQL, поэтому загружает все объекты в память и фильтрует в PHP. Я обновлю свой ответ примером.
Отлично, это то, на чем я могу основываться. Большое спасибо.
Нет проблем, рад, что смог помочь!
отсутствует закрытие ) в $criteria->where(Criteria::expr()->eq('kind', 'master');, не могли бы вы исправить. Я не могу сохранить правки, содержащие изменение только одной буквы...
Я думаю, что самый простой способ - это собственный метод репозитория для этого.