我有两个实体。
首先是我的父实体,它具有名为 productsCount 的属性。此实体具有链接到它的另一个实体,称为"存储",并且"存储已链接房间"。每个房间可以有多个产品。
当我编辑分配给房间的产品时,我想更新父产品计数以存储所有商店中所有房间中所有产品的数量。
我有一个计算计数的 SQL 查询。每次我用新产品更新房间时,我都需要这样做。这是使用预刷新挂钩在房间实体上使用实体侦听器完成的。
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();
}
查询应该工作正常,所以我认为它与刷新和保留父实体有关。
有什么想法吗?
所以,经过几个小时的战斗,我找到了解决方案。
事实证明,我不必坚持,也不必刷新更改。解决方案是使用工作单元来重新计算$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);
}
原因是递归:您从订阅者调用$em->flush();
,EntityManager 进入 flush
,触发preFlush
调用您的处理程序的事件,该处理程序再次调用$em->flush()
等等。
IIRC preFlush 是在更改集计算之前调用的,因此只需使用新值更新实体就足以让 Doctrine 检测到所述更改。