symfony2 Maximum function nesting level of '10

2019-05-23 14:02发布

I have eventlistener preUpdate

public function preUpdate(PreUpdateEventArgs $args) {        
    $user = $args->getEntity();
    if($user instanceof \iTracker\UserBundle\Entity\User) {
        if($args->hasChangedField('userGroup')) {

            $old = $args->getOldValue('userGroup');
            $new = $args->getNewValue('userGroup');

            $em = $args->getEntityManager();

            $old->setAmount($old->getAmount() - 1);
            $em->persist($old);

            $new->setAmount($new->getAmount() + 1);
            $em->persist($new);
            $em->flush();
        }
    }
}

And after submiting form i get FatalErrorException: Error: Maximum function nesting level of '100' reached, aborting! in /var/www/issue/app/cache/dev/classes.php line 6123

in /var/www/issue/app/cache/dev/classes.php line 6123 at ErrorHandler->handleFatal() in /var/www/issue/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Debug/ErrorHandler.php line 0 at NormalizerFormatter->normalize() in /var/www/issue/app/cache/dev/classes.php line 6198 at LineFormatter->normalize() in /var/www/issue/app/cache/dev/classes.php line 6112 at NormalizerFormatter->format() in /var/www/issue/app/cache/dev/classes.php line 6172 at LineFormatter->format() in /var/www/issue/app/cache/dev/classes.php line 6320 at AbstractProcessingHandler->handle() in /var/www/issue/app/cache/dev/classes.php line 6646 at Logger->addRecord() in /var/www/issue/app/cache/dev/classes.php line 6710 at Logger->debug() in /var/www/issue/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php line 72 at DbalLogger->log() in /var/www/issue/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php line 50 at DbalLogger->startQuery() in /var/www/issue/vendor/doctrine/dbal/lib/Doctrine/DBAL/Logging/LoggerChain.php line 50 at LoggerChain->startQuery() in /var/www/issue/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php line 774 at Connection->executeUpdate() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 447 at BasicEntityPersister->_updateTable() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 357 at BasicEntityPersister->update() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 984 at UnitOfWork->executeUpdates() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 317 at UnitOfWork->commit() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php line 355 at EntityManager->flush() in /var/www/issue/src/iTracker/UserBundle/Listener/UserGroupAmount.php line 41 and this

  • at UserGroupAmount->preUpdate() in /var/www/issue/vendor/symfony/symfony/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php line 61
  • at ContainerAwareEventManager->dispatchEvent() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 980
  • at UnitOfWork->executeUpdates() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 317
  • at UnitOfWork->commit() in /var/www/issue/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php line 355
  • at EntityManager->flush() in /var/www/issue/src/iTracker/UserBundle/Listener/UserGroupAmount.php line 41

and this 5 errors are looped, which cause this exception

2条回答
Bombasti
2楼-- · 2019-05-23 14:39

@meze is right, using flush inside the *Flush* events will cause a loop.

But there is a quick workaround that will allow you to do it still: $eventManager = $this -> em -> getEventManager();

// Remove event, if we call $this->em->flush() now there is no infinite recursion loop!
$eventManager -> removeEventListener('onFlush', $this); 

// ...

// Re-attach since we're done
$eventManager -> addEventListener('onFlush', $this); 

Seen on: Doctrine2 Event Listener: persisting in onFlush() | Benedikt Wolters

查看更多
我命由我不由天
3楼-- · 2019-05-23 14:51

Calling flush suggested by the author is not a correct solution. It will fire onFLush twice and create unneeded savepoints in transaction.

All additional changes can be scheduled in events using computeChangeSet, recomputeSingleEntityChangeSet and scheduleExtraUpdate methods.

In case with a different entity:

public function preUpdate(PreUpdateEventArgs $args) {        
    $user = $args->getEntity();
    if($user instanceof \iTracker\UserBundle\Entity\User) {
        if($args->hasChangedField('userGroup')) {
            $old = $args->getOldValue('userGroup');
            $new = $args->getNewValue('userGroup');

            $oldOriginAmount = $old->getAmount();
            $newOriginAmount = $new->getAmount();

            $old->setAmount($old->getAmount() - 1);
            $uow->scheduleExtraUpdate($old, array(
                'amount' => array($oldOriginAmount, $old->getAmount())
            ));

            $new->setAmount($new->getAmount() + 1);
            $uow->scheduleExtraUpdate($new, array(
                'amount' => array($newOriginAmount, $new->getAmount())
            ));
        }
    }
}

Calling persist is not needed (because the associated entites aren't created in any event, they should be already persistent).

查看更多
登录 后发表回答