尝试保存附加实体时,Doctrine事件侦听器中的无限循环 [英] Infinite loop in Doctrine event listener when trying to save additional entity

查看:154
本文介绍了尝试保存附加实体时,Doctrine事件侦听器中的无限循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望每次新的距离实体(从Place_A到Place_B)被保存,反向距离(从Place_B到Place_A)将数据插入到数据库中

I want that every time a new Distance entity (from Place_A to Place_B) is saved, the reverse distance (from Place_B to Place_A) gets inserted too into the DB.

我的问题是以下听众无限循环(因此,计数器):

My problem is the following listener loops infinitely (hence the counter):

class Listener
{
    public $count;

    public function prePersist(LifecycleEventArgs $eventArgs)
    {
        if ($this->count > 5) {
            die();
        }

        $entity = $eventArgs->getEntity();

        if ($entity instanceof Distance) {
            // $this->created = microtime(true) in Distance's constructor
            echo 'Entity created at ' . $entity->created;

            if ($entity->isReverse) {
                echo " is reverse\n";
            } else {
                echo " is not reverse\n";
                $this->count++;

                $reverse = new Distance();
                $reverse->setOrigin($entity->getDestination());
                $reverse->setDestination($entity->getOrigin());
                $reverse->set($entity->getMiles());
                $reverse->isReverse = true;

                $em = $eventArgs->getEntityManager();
                $em->persist($reverse);
                $em->flush();
            }
        }
    }
}

输出:

Entity created at 1433168310.8787 is not reverse
Entity created at 1433168310.9073 is reverse
Entity created at 1433168310.8787 is not reverse
Entity created at 1433168310.9078 is reverse
Entity created at 1433168310.8787 is not reverse
Entity created at 1433168310.908 is reverse
Entity created at 1433168310.8787 is not reverse
Entity created at 1433168310.9084 is reverse
Entity created at 1433168310.8787 is not reverse
Entity created at 1433168310.9087 is reverse
Entity created at 1433168310.8787 is not reverse

它像原始实体(创建时间以8787结尾)持续无限次。

It's like the original entity (creation time ending with 8787) was persisted an infinite number of times.

如果我删除了对 $ em-> flush 的调用,我正确地得到以下输出:

Just in case, if I remove the call to $em->flush, I correctly get the following output:

Entity created at 1433167824.2552 is not reverse
Entity created at 1433167824.2947 is reverse

但是,参数绑定到插入查询。 Symfony的分析器证实了这一点:

but then an exception saying that no parameters were bound to the insert query. Which is confirmed by Symfony's profiler:

INSERT INTO Distance (
    miles, origin_id, destination_id
) 
VALUES 
(?, ?, ?)
Parameters: { }

我想了解为什么我的听众不按预期工作,以及如何解决它。

I'd like to understand why my listener doesn't work as I expect, and how to fix it.

根据要求,这里有一些更多的代码。一切都来自一个地方表格,除了输入地名,我可以添加/删除/编辑距离的集合到其他 Place s。

As requested, here's some more code. Everything comes from a Place Form where, apart from entering the Place name, I can add/delete/edit a collection of distances to other Places.

// PlaceController::updateAction
public function updateAction(Request $request, $id)
{
    $em = $this->getDoctrine()->getManager();

    $entity = $em->getRepository('MyBundle:Place')->find($id);
    if (! $entity) {
        throw $this->createNotFoundException('Unable to find Place entity.');
    }

    $deleteForm = $this->createDeleteForm($id);
    $editForm = $this->createForm(new PlaceType(), $entity, array(
        'action' => $this->generateUrl('update_place', array('id' => $entity->getId())),
        'method' => 'PUT'
    ));
    $editForm->add('submit', 'submit', array('label' => 'panel.button.save'));

    $editForm->handleRequest($request);

    if ($editForm->isValid()) {
        $em->flush();

    return array(
        'entity' => $entity,
        'form' => $editForm->createView(),
        'delete_form' => $deleteForm->createView(),
    );
}

// PlaceType::buildForm
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $Place = $builder->getData();

    $builder
        ->add(
            'name',
            'text',
            [
                'label' => 'object.place.name'
            ]
        )
        ->add(
            'distancesTo',
            'collection',
            [
                'label' => 'object.place.distance.plural',
                'type' => new DistanceType(),
                'by_reference' => false,
                'allow_add' => true,
                'allow_delete' => true,
                'options' => [
                    'required' => false,
                    'origin' => $Place->getId() ? $Place : null
                ]
            ]
        );
}


推荐答案

你不应该使用$ em - > flush()里面prePersist,它被Doctrine限制: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#reference-events-implementing-listeners

You should not use $em->flush() inside prePersist, it is restricted by Doctrine: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#reference-events-implementing-listeners

有关于preUpdate的信息,但是对于prePesist调用

There is information about preUpdate, but same situation (loop) is applied for prePesist call

9.6.6应用了相同的情况(循环)。 preUpdate

9.6.6. preUpdate


PreUpdate是最受限制的使用事件,因为在调用实体的更新语句之前调用

EntityManager#flush()方法之内。
更改实体的关联在这个>事件中是永远不允许的,因为Doctrine不能保证在刷新操作的这一点上正确地处理引用的完整性。

PreUpdate is the most restrictive to use event, since it is called right before an update statement is called for an entity inside the EntityManager#flush() method. Changes to associations of the updated entity are never allowed in this >event, since Doctrine cannot guarantee to correctly handle referential ?integrity at this point of the flush operation.

类似的描述在这里描述 preUpdate()兄弟姐妹管理成树:如何断开 - > persist()递归?

Similar sitaution is described here preUpdate() siblings manage into tree: how to break ->persist() recursion?

所以您还可以做相似的方式:创建自定义事件,创建自定义事件订户,您将在其中创建反向实体并在控制器操作上分派该事件订阅者。

So you can also do similar way: create custom event, create custom event subscriber where you will create reverse entity and dispatch that event subscriber at the controller action.

这篇关于尝试保存附加实体时,Doctrine事件侦听器中的无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆