php - 尝试保存其他实体时 Doctrine 事件监听器中的无限循环

标签 php symfony orm doctrine-orm doctrine

我希望每次保存新的 Distance 实体(从 Place_A 到 Place_B)时,都会得到相反的距离(从 Place_B 到 Place_A) gets inserted too into the DB .

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

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 结尾)被保留了无数次。

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

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

但随后出现异常,表示没有参数绑定(bind)到插入查询。 Symfony 的分析器证实了这一点:

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

我想了解为什么我的监听器不能按我的预期工作,以及如何修复它。


根据要求,这里有更多代码。一切都来自地点表单,除了输入地点名称之外,我还可以添加/删除/编辑到其他地点的距离集合。

// 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
                ]
            ]
        );
}

最佳答案

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

有关于 preUpdate 的信息,但同样的情况(循环)适用于 prePesist 调用

9.6.6。预更新

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() siblings manage into tree: how to break ->persist() recursion?

因此,您也可以采用类似的方式:创建自定义事件,创建自定义事件订阅者,您将在其中创建反向实体并在 Controller 操作处分派(dispatch)该事件订阅者。

关于php - 尝试保存其他实体时 Doctrine 事件监听器中的无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30576245/

相关文章:

php - 是否有可能在 PHP 中过度使用后期静态绑定(bind)?

php - 如何处理两次编码的 JSON?

java - hibernate 后初始化

java - Hibernate/JPA - 注释 bean 方法和字段

php - 带 CKEditor 的文件管理器

php - 如何为 HTML 表格中的每一行添加 jQuery 对话框

php - 将 JSON 对象发布到 Symfony 2

symfony - 使用 PHPUnit 3.7.7 和 Symfony2 时出错

python - Laravel 应用程序使用 Symfony 在 LEMP 堆栈上执行 Python 脚本 - 权限被拒绝

java - 当父子有不同的Id时如何实现 "is–a"关系