2015-06-01 57 views
2

我希望每次保存一個新的Distance實體(從Place_A到Place_B),反向距離(從Place_B到Place_A)gets inserted too into the DB無限循環在Doctrine事件監聽器試圖保存附加實體時

我的問題是下面的聽衆無限循環(因此計數器):

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 

但隨後一個異常說,沒有參數綁定到插入查詢。這是由Symfony的分析器證實:

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

我想了解爲什麼我的聽衆不能按我的預期工作,以及如何解決它。


按照要求,這裏的一些更多的代碼。一切都來自於Place表格,除了輸入地名,我可以添加/刪除/編輯其他Place的距離集合。

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

那麼首先你不應該有一個調用''em-> flush()'無論如何,因爲它是一個糟糕的形式在prePersist塊中刷新,因爲實體管理器可能會刷新原始位置被堅持。 – DIMMSum

+1

你可以添加原始實體持續存在的代碼嗎? – DIMMSum

+0

@Decave堅持原始距離實體的代碼不是我的。如我的編輯中所解釋的,當持久化父級Place實體(關聯中的「cascade:{」persist「})時會自動發生。 – marcv

回答

2

,則不應使用EM-$>的flush()內prePersist,它是由學說限制:http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#reference-events-implementing-listeners

有關於更新前的信息,但同樣的情況(循環)被應用於prePesist呼叫

9.6.6。更新前

更新前的是最嚴格的使用事件,因爲它被稱爲 的更新語句呼籲 的EntityManager#flush()方法中的實體權利之前。 在此事件中絕不允許更新已更新實體的關聯,因爲Doctrine無法保證在刷新操作的這一點上正確處理參照完整性。

類似sitaution這裏描述preUpdate() siblings manage into tree: how to break ->persist() recursion?

所以,你也可以做類似的方法:創建自定義事件,創建自定義事件訂閱將在其中創建反向實體並分派該事件的用戶在控制器動作。