2012-05-29 14 views
22

Ich muss überprüfen, ob sich eine persistente Entität geändert hat und in der Datenbank aktualisiert werden muss. Was ich gemacht (und funktionierte nicht) war die folgende:Wie überprüft man, ob Entität in Doctrine 2 geändert wurde?

$product = $entityManager->getRepository('Product')->find(3); 
$product->setName('A different name'); 

var_export($entityManager->getUnitOfWork()->isScheduledForUpdate($product)); 

Dieser Code druckt immer false, versuchte ich auch, bevor überprüfen die Arbeitseinheit zu spülen, aber hat nicht funktioniert.

hat jemand einen Vorschlag?

+0

Was versuchen Sie zu erreichen, wenn Sie feststellen, dass sich eine Entität geändert hat? –

+0

Wenn Sie sich den Quelltext von 'UnitOfWork' anschauen, sagen sie in den Kommentaren von' isScheduledForUpdate' "Hinweis: Ist momentan nicht sehr nützlich, da schmutzige Entities nur zur Commit Zeit registriert werden" –

+0

Nun ... Ich muss eine senden Benachrichtigung an einen Systembenutzer, wenn das Produkt geändert wird, aber ich sollte die Benachrichtigung ignorieren, wenn die Produktdaten nicht geändert werden (ich spüe trotzdem nach dem Binden der POST-Daten meines Formulars an die Entität, um diese Doctrine-Leichtigkeit zu verwenden). Ich werde versuchen, UnitOfWork :: computeChangeSets() vorher auszuführen, möglicherweise Leistung fallen lassen, aber kann arbeiten. – eagleoneraptor

Antwort

21

Das erste, was ich überprüfen würde, dass Ihre setName Funktion tatsächlich etwas tut ($ this-> name = $ name ...) Wenn es bereits funktioniert, dann könnten Sie einen Ereignis-Listener auf Ihre Dienste definieren .yml, die ausgelöst wird, wenn Sie den Flush aufrufen.

entity.listener: 
    class: YourName\YourBundle\EventListener\EntityListener 
    calls: 
    - [setContainer, ["@service_container"]] 
    tags: 
    - { name: doctrine.event_listener, event: onFlush } 

Dann definieren Sie die EntityListener

namespace YourName\YourBundle\EventListener; 

use Doctrine\ORM\Event; 
use Symfony\Component\DependencyInjection\ContainerAware; 

class EntityListener extends ContainerAware 
{ 

    /** 
    * Gets all the entities to flush 
    * 
    * @param Event\OnFlushEventArgs $eventArgs Event args 
    */ 
    public function onFlush(Event\OnFlushEventArgs $eventArgs) 
    { 
     $em = $eventArgs->getEntityManager(); 
     $uow = $em->getUnitOfWork(); 

     //Insertions 
     foreach ($uow->getScheduledEntityInsertions() as $entity) { 
      # your code here for the inserted entities 
     } 

     //Updates 
     foreach ($uow->getScheduledEntityUpdates() as $entity) { 
      # your code here for the updated entities 
     } 

     //Deletions 
     foreach ($uow->getScheduledEntityDeletions() as $entity) { 
      # your code here for the deleted entities 
     } 
    } 
} 

Wenn Sie wissen müssen, welche Elemente geändert werden, sondern etwas mit ihnen nach haben sie in der Datenbank gespeichert sind, nur speichern die Entitäten, die in einem privaten Array geändert wurden, und dann ein onFlush-Ereignis definieren, das die Entitäten aus dem Array abruft.

BTW, diese Art von Ereignissen auslösen Sie die @ORM \ HasLifecycleCallbacks auf die Entität hinzufügen müssen.

+0

Behalten Sie im Hinterkopf, dass Entitäten angepasst/eingefügt/gelöscht werden und dass Sie mit dem onFlush-Ereignis nicht sicher sein können, dass der Flush erfolgreich ist. – Jekis

+0

Es gibt auch '$ uow-> isEntityScheduled()' das alle Operationen abdeckt. –

+0

Hi, genau das suche ich, aber mit einem Unterschied: Ich muss wissen, welche Tabelle/Daten sich geändert haben und sie in eine andere Tabelle einfügen , so etwas wie das Protokollieren jeder Änderung in der Datenbank, kannst du mir dabei helfen? https://stackoverflow.com/questions/44974520/symfony3-save-every-update-intodatabase –

10

Sie können sich auch an der PreUpdate Veranstaltung suchen möchten, wenn Sie den Zugriff auf Unternehmens Felder mit ihren alten und neuen Werte müssen.

Ein bisschen Beispiel überwiegend aus dem darin enthaltenen Link genommen:

<?php 
class NeverAliceOnlyBobListener 
{ 
    public function preUpdate(PreUpdateEventArgs $eventArgs) 
    { 
     if ($eventArgs->getEntity() instanceof User) { 
      if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') { 
       $oldValue = $eventArgs->getOldValue('name'); 
       $eventArgs->setNewValue('name', 'Bob'); 
      } 
     } 
    } 
} 
+0

das ist was ich gesucht habe. Prost – Sharpy35

8

ich nicht/brauchte Zuhörer für meinen Fall zu schaffen, so landete ich mit

$product->setName('A different name'); 
$uow = $entityManager->getUnitOfWork(); 
$uow->computeChangeSets(); 
if ($uow->isEntityScheduled($product)) { 
    // My entity has changed 
} 
+0

Gibt wahr für mich zurück, wenn sich nichts an der Entität geändert hat. – Jessica

+0

@Jessica, vielleicht wird Ihre Entität woanders geändert, zum Beispiel in einem Lebenszyklus Rückruf. In jedem Fall sollte das Objekt $ uow in dieser Antwort anzeigen, was für diese Entität unter der Eigenschaft 'changesets' geändert wurde. – Nicolas

+0

Heads up, 'isEntityScheduled ($ entity)' gibt 'true' zurück, wenn eine zugehörige Entity Änderungen aufweist. Z.B. Sie haben 'Produkt # Marke', wobei' Marke Nukeface

2

Das Problem ist ziemlich alt, aber es kann immer noch einige Gruppe von Menschen, die dieses Problem aus einer anderen Sicht konfrontiert sein könnten. Die UnitOfWork funktioniert gut, aber es gibt nur das Array der Änderungen zurück. Es kann ein Schmerz im Hintern sein, wenn jemand nicht wirklich weiß, welche Felder sich geändert haben könnten und nur die ganze Entität als ein Objekt zum Vergleichen $oldEntity und $newEntity erhalten möchte. Auch wenn der Name der Veranstaltung Preupdate ist, wenn jemand versuchen, die Daten aus der Datenbank zu holen, wie folgt:

$er->find($id); 

der zurück Unternehmen alle Änderungen enthalten. Die Abhilfe ist ziemlich einfach, aber es hat einige Haken:

public function preUpdate(Entity $entity, PreUpdateEventArgs $args) 
{ 
    $entity = clone $entity; //as Doctrine under the hood 
          //uses reference to manage entities you might want 
          //to work on the entity copy. Otherwise,   
          //the below refresh($entity) will affect both 
          //old and new entity. 
    $em = $args->getEntityManager(); 
    $currentEntity = $em->getRepository('AppBundle:Entity')->find($entity->getId()); 
    $em->refresh($currentEntity); 

} 

Für diejenigen, die ein anderes Ereignis verwenden, wie Vorspülen, ich habe es schnell überprüft und die Abhilfe gut funktionierte nicht, weil wahrscheinlich die refresh() Methode verwirft Jede Flush-Änderung, also was getan werden muss, ist, den Flush erneut im Listener aufzurufen und einen statischen $alreadyFlushed-Toggle zu erzeugen, um eine zirkuläre Referenz zu vermeiden.

0

Ich bin neugierig auf Doctrine und alle, die postFlush dokumentieren, denn in einigen Fällen haben Sie eine laufende Transaktion. Ich möchte darauf hinweisen, dass es auch postTransactionCommit gibt, was je nachdem, was Sie im postFlush-Ereignis erreichen möchten, sicherer sein könnte.

0

Basierend auf meinen Bedürfnissen, Antworten hier und the docs, kam ich mit der folgenden Lösung für einen modifiedAt Zeitstempel in einer Entität.

/** 
* @Doctrine\ORM\Mapping\PreUpdate() 
* 
* @param \Doctrine\ORM\Event\PreUpdateEventArgs $args 
* @return $this 
*/ 
public function preUpdateModifiedAt(\Doctrine\ORM\Event\PreUpdateEventArgs $args) 
{ 
    $this->setModifiedAt(new \DateTime('now')); 

    return $this; 
} 

Dies basiert auf what the docs say diesen Event zu den anderen verfügbaren Gegensatz wie PostPersist und PreFlush:

Preupdate die restriktivsten ist Ereignis zu bedienen, es da Recht genannt wird bevor eine update-Anweisung für eine Entität innerhalb der EntityManager # flush() -Methode aufgerufen wird. Beachten Sie, dass dieses Ereignis nicht ausgelöst wird, wenn das berechnete Änderungsset leer ist.

Mit PreUpdate als Gegensatz zu den anderen können Sie verlassen alle Berechnungen und rechenintensiven Funktionen für den Prozess bereits durch Lehre definiert. Das manuelle Auslösen der Berechnung von Changesets, wie zum Beispiel in theseanswers oben, sind Server-CPU-intensiv. Das onFlush-Ereignis, wie es in the accepted answer verwendet wird, ist eine Option (in der dargestellten Weise), aber nicht, wenn Sie sich auf eine Änderung der Entität verlassen, wie Sie es mit der obigen Funktion tun können (preUpdateModifiedAt(PreUpdateEventArgs $args)).

Verwandte Themen