2015-06-09 9 views
8

Zur Klarheit fahre ich hier fort die Diskussion begann here.Persistent andere Entitäten in PreUpdate von Doctrine Entity Listener

In einem Doctrine Entity Listener, in der preUpdate-Methode (wo ich auf den alten und neuen Wert eines Feldes der Entität zugreifen kann) versuche ich eine Entität persistent zu sein, die nicht mit der fokalen Einheit verwandt ist.

Grundsätzlich habe ich Entität A, und wenn ich einen Wert in einem der Felder, die ich schreiben möchte, in der Projektnotifikationstabelle die Felder oldValue, newValue und andere ändern.

Wenn ich die preUpdate-Methode nicht lösche, wird die neue Benachrichtigungsentität nicht in der DB gespeichert. Wenn ich es spüle, gehe ich in eine Endlosschleife.

Dies ist die Preupdate Methode:

public function preUpdate(ProjectTolerances $tolerances, PreUpdateEventArgs $event) 
{ 
    if ($event->hasChangedField('riskToleranceFlag')) { 
    $project = $tolerances->getProject();     
    $em = $event->getEntityManager(); 
    $notification = new ProjectNotification(); 
    $notification->setValueFrom($event->getOldValue('riskToleranceFlag')); 
    $notification->setValueTo($event->getNewValue('riskToleranceFlag')); 
    $notification->setEntity('Entity'); //TODO substitute with the real one 
    $notification->setField('riskToleranceFlag'); 
    $notification->setProject($project); 
    $em->persist($notification); 


    // $em->flush(); // gives infinite loop 
    } 
} 

ein bisschen Googeln ich entdeckte, dass Sie nicht die Spülung in den Hörer anrufen können, und here ist es vorgeschlagen, das Zeug zu speichern in einem Array beibehalten werden, es zu spülen später im onFlush. Nichtsdestoweniger funktioniert es nicht (und wahrscheinlich sollte es nicht funktionieren, da die Instanz der Listener-Klasse nach dem Aufruf des preUpdate zerstört wird. Also geht alles, was Sie als geschütztes Attribut auf der Ebene der Klasse speichern, verloren, wenn Sie später onFlush aufrufen oder fehle ich etwas?). Hier

ist die aktualisierte Version des Zuhörers:

class ProjectTolerancesListener 
{ 
    protected $toBePersisted = []; 

    public function preUpdate(ProjectTolerances $tolerances, PreUpdateEventArgs $event) 
    { 
     $uow = $event->getEntityManager()->getUnitOfWork(); 
//  $hasChanged = false; 

     if ($event->hasChangedField('riskToleranceFlag')) { 
     $project = $tolerances->getProject();     
     $notification = new ProjectNotification(); 
     $notification->setValueFrom($event->getOldValue('riskToleranceFlag')); 
     $notification->setValueTo($event->getNewValue('riskToleranceFlag')); 
     $notification->setEntity('Entity'); //TODO substitute with the real one 
     $notification->setField('riskToleranceFlag'); 
     $notification->setProject($project); 

     if(!empty($this->toBePersisted)) 
      { 
      array_push($toBePersisted, $notification); 
      } 
     else 
      { 
      $toBePersisted[0] = $notification; 
      } 
     } 
    } 

    public function postFlush(LifecycleEventArgs $event) 
    { 
     if(!empty($this->toBePersisted)) { 

      $em = $event->getEntityManager(); 

      foreach ($this->toBePersisted as $element) { 

       $em->persist($element); 
      } 

      $this->toBePersisted = []; 
      $em->flush(); 
     } 
    } 
} 

Vielleicht kann ich dieses Problem lösen, indem sie ein Ereignis aus dem Inneren des Zuhörers mit allen benötigten Informationen Brennen meiner Holzeinschlag nach dem Flush durchführen ... aber :

1) ich weiß nicht, ob ich es

2) tun kann, scheint es ein wenig zuviel des Guten

Thank you!

Antwort

10

Verwenden Sie nicht preUpdate, verwenden Sie onFlush - dies ermöglicht Ihnen den Zugriff auf die UnitOfWork API & Sie können dann Entitäten persisten.

z. (Das ist, wie ich es tun 2.3, könnten in neueren Versionen geändert werden)

$this->getEntityManager()->persist($entity); 
    $metaData = $this->getEntityManager()->getClassMetadata($className); 
    $this->getUnitOfWork()->computeChangeSet($metaData, $entity); 
+1

Haben Sie im onFlush Zugriff auf oldValue und newValue? –

+2

Sicher tun. UnitOfWork hat dafür getEntityChangeSet - http://www.doctrine-project.org/api/orm/2.0/class-Doctrine.ORM.UnitOfWork.html – Richard

19

Ich gebe alle Kredite an Richard für mich in die richtige Richtung, so dass ich nehme seine Antwort. Trotzdem veröffentliche ich meine Antwort mit dem kompletten Code für zukünftige Besucher.

class ProjectEntitySubscriber implements EventSubscriber 
{ 
    public function getSubscribedEvents() 
    { 
     return array(
      'onFlush', 
     ); 
    } 

    public function onFlush(OnFlushEventArgs $args) 
    { 
     $em = $args->getEntityManager(); 
     $uow = $em->getUnitOfWork(); 

     foreach ($uow->getScheduledEntityUpdates() as $keyEntity => $entity) { 
      if ($entity instanceof ProjectTolerances) { 
       foreach ($uow->getEntityChangeSet($entity) as $keyField => $field) { 
        $notification = new ProjectNotification(); 
        // place here all the setters 
        $em->persist($notification); 
        $classMetadata = $em->getClassMetadata('AppBundle\Entity\ProjectNotification'); 
        $uow->computeChangeSet($classMetadata, $notification); 
       } 
      } 
     } 
    } 
} 
Verwandte Themen