У меня есть две сущности.
Во-первых, это моя родительская сущность, у которой есть свойство под названием productsCount. У этой сущности есть другая сущность, связанная с ней, которая называется Магазин, и у магазина есть связанная комната. В каждой комнате может быть несколько продуктов.
Когда я редактирую продукты, назначенные комнате, я хочу обновить Parent productsCount, чтобы сохранить количество всех продуктов во всех комнатах во всех магазинах.
У меня есть SQL-запрос, который вычисляет количество. Мне нужно делать это каждый раз, когда я обновляю комнату новыми продуктами. Это делается с помощью хука preFlush с использованием EntityListener на объекте Room.
Хук preFlush срабатывает правильно, но по какой-то причине время ожидания истекает.
Вот пример кода preFlush
public function preFlush(Room $room, PreFlushEventArgs $args)
{
$em = $args->getEntityManager();
$parent = $room->getStore()->getParent();
$rsm = new ResultSetMapping();
$rsm->addScalarResult('COUNT(product_id)', 'count');
$query = $em->createNativeQuery(
'select COUNT(product_id)
from room_product where `room_id` in
(select id from room where store_id in
(select id from store where parent_id = :parentId))', $rsm);
$query->setParameter('parentId', $parent->getId());
$result = $query->getOneOrNullResult();
$parent->setNumberOfServices($result['count']);
$em->persist($parent);
$em->flush();
}
Запрос должен работать нормально, поэтому я думаю, что это как-то связано с очисткой и сохранением родительского объекта.
Любые идеи?





Причина в рекурсии: вы вызываете $em->flush(); от подписчика, EntityManager входит в flush, запускает событие preFlush, которое вызывает ваш обработчик, который снова вызывает $em->flush() и так далее.
IIRC preFlush вызывается перед расчетом набора изменений, поэтому простого обновления вашего объекта новым значением должно быть достаточно, чтобы Doctrine могла обнаружить указанное изменение.
Заставить Doctrine пересчитать свой набор изменений с помощью пересчитатьSingleEntityChangeSet
Итак, я смог найти решение после нескольких часов борьбы.
Оказывается, мне не нужно ни сохраняться, ни сбрасывать изменения. Решение состоит в том, чтобы использовать Unit of Work для пересчета изменений в $parent, а затем доктрина сбросит их самостоятельно. Мне также пришлось изменить способ подсчета продуктов, так как на этапе preFlush изменения еще нет в базе данных, поэтому запрос не будет работать должным образом. Поэтому я считаю их вручную, обходя дерево отношений.
Вот пример кода.
public function preFlush(Room $room, PreFlushEventArgs $args)
{
$em = $args->getEntityManager();
$numOfProducts = 0;
$parent = $room->getStore()->getParent();
foreach($parent->getStores() as $store) {
foreach($store->getRooms() as $room) {
$numOfServices += count($room->getProducts());
}
}
$parent->setNumberOfProducts($numOfProducts);
$classMetadata = $em->getClassMetadata(get_class($parent));
$em->getUnitOfWork()->computeChangeSet($classMetadata, $parent);
}
Я закомментировал сохранение и сброс, но тогда $parent не сохраняется.