2012-05-29 21 views
22

Necesito comprobar si una entidad persistente ha cambiado y necesita ser actualizada en la base de datos. Lo hice (y no funcionó) fue la siguiente:¿Cómo verificar si la entidad ha cambiado en Doctrine 2?

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

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

que imprime códigos siempre false, también trataron de limpiar antes de comprobar la unidad de trabajo, pero no funcionó.

¿Alguien tiene alguna sugerencia?

+0

¿Qué intenta lograr al descubrir que una entidad ha cambiado? –

+0

Si nos fijamos en el código fuente de 'UnitOfWork', en los comentarios de' isScheduledForUpdate' dicen "Nota: No es muy útil actualmente ya que las entidades sucias solo se registran en el momento de la confirmación" –

+0

Bueno ... Tengo que enviar un notificación a un usuario del sistema cuando se cambia el producto, pero debería ignorar la notificación cuando los datos del producto no se modifiquen (de todos modos me pongo de color luego de vincular los datos POST de mi formulario a la entidad para usar esa facilidad de Doctrine). Voy a tratar de ejecutar UnitOfWork :: computeChangeSets() antes, tal vez dejar caer el rendimiento pero puedo trabajar. – eagleoneraptor

Respuesta

21

Lo primero que comprobaría es que su función setName está haciendo algo ($ this-> name = $ name ...) Si ya está funcionando, podría definir un detector de eventos en sus servicios .yml que se activa cuando llamas al color.

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

A continuación, se define el 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 
     } 
    } 
} 

Si lo que necesita saber qué entidades están siendo cambiadas, pero hacer algo con ellos después de que han sido guardados en la base de datos, simplemente almacenar el entidades cambiadas en una matriz privada, y luego definen un evento onFlush que obtiene las entidades de la matriz.

BTW, para activar este tipo de eventos necesita agregar @ORM \ HasLifecycleCallbacks en la entidad.

+0

Tenga en cuenta que las entidades SERÁN udpatadas/insertadas/eliminadas y, utilizando el evento Flush, no puede estar seguro de que la descarga sea exitosa. – Jekis

+0

También hay '$ uow-> isEntityScheduled()' que cubre todas las operaciones –

+0

Hola, esto es exactamente lo que estoy buscando, pero con un diff: necesito saber qué tabla/datos ha cambiado e insertarlo en otra tabla , algo así como registrar cada cambio en la base de datos, ¿me pueden ayudar con esto? https://stackoverflow.com/questions/44974520/symfony3-save-every-update-into-database –

10

Es posible que también desee consultar el evento PreUpdate, si necesita acceder a los campos de entidad con sus valores antiguos y nuevos.

Un poco de un ejemplo tomado de su mayoría en el enlace proporcionado:

<?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

esto es lo que estaba buscando. cheers – Sharpy35

8

no necesitaba/oyentes quieren crear para mi caso por lo que terminaron con

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

Devuelve verdadero para mí cuando nada en la entidad ha cambiado. – Jessica

+0

@Jessica, tal vez su entidad se modifique en otro lugar, por ejemplo, en una llamada de ciclo de vida. En cualquier caso, el objeto $ uow en esta respuesta debe indicar qué se ha cambiado para esa entidad bajo la propiedad 'changesets' – Nicolas

+0

Heads up, 'isEntityScheduled ($ entity)' devuelve 'true' si una Entidad relacionada tiene cambios. P.ej. tiene 'Product # brand', donde' # brand' es una 'Brand' Entity. Si modifica el 'Nombre de marca #' de un campo infantil de' ProductFieldset' y no tiene cambios en el fieldset base de 'Product', tanto las entidades' Product' como 'Brand' tendrán una marca de tiempo 'modifiedAt' actualizada. . – Nukeface

2

Doctrine2 Docs. 17. Change Tracking Policies

Si utilizo el tercer formulario (17.3. Notificar) como lo hago, puede probar si su entidad se ha cambiado haciendo:

$uow = $entityManager->getUnitOfWork(); 
$aChangeSet = $uow->getEntityChangeSet($oEntity); 

Si nada ha cambiado, devolverá una matriz en blanco.

2

El problema es bastante antiguo, pero aún puede haber algún grupo de personas que pueda enfrentar este problema desde un punto de vista diferente. El UnitOfWork funciona muy bien, pero solo devuelve la matriz de cambios. Puede ser una molestia cuando alguien no sabe realmente qué campos pueden haber cambiado y solo quiere obtener la entidad completa como un objeto para comparar $oldEntity y $newEntity. A pesar de que el nombre del evento es PreUpdate si alguien va a tratar de recuperar los datos desde la base de datos de la siguiente manera:

$er->find($id); 

la entidad devuelta contendrá todos los cambios. La solución es bastante simple, pero tiene algunos ganchos:

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); 

} 

Para aquellos que están utilizando otro evento, como pre-limpieza, He comprobado rápidamente y la solución no funcionó bien porque probablemente los descartes método refresh() cualquier cambio de color cambia, por lo que lo que hay que hacer es llamar al color una vez más en el oyente y crear un alternador estático $alreadyFlushed para evitar la referencia circular.

0

Tengo curiosidad acerca de Doctrine y de todos los que están documentando postFlush, ya que, en algunos casos, tiene una transacción en curso. Me gustaría señalar que también hay postTransactionCommit, que podría ser más seguro dependiendo de lo que intentes lograr en el evento postFlush.

0

Según mis necesidades, las respuestas aquí y the docs, se me ocurrió la siguiente solución para una marca de tiempo modifiedAt en una entidad.

/** 
* @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; 
} 

Esto se basa en what the docs say sobre este Event a diferencia de las otras disponibles, tales como PostPersist y PreFlush:

PreUpdate es el más restrictivo de usar caso, ya que se llama derecha antes de que se invoque una declaración de actualización para una entidad dentro del método EntityManager # flush(). Tenga en cuenta que este evento no se desencadena cuando el conjunto de cambios calculado está vacío.

Usando PreUpdate a diferencia de los demás le permite dejar todos los cálculos y funciones intensivas de cálculo para el proceso ya definido por la doctrina. El cómputo de desencadenamiento manual de conjuntos de cambios, como en theseanswers anterior, es intensivo en la CPU del servidor. El evento onFlush, como el utilizado en the accepted answer es una opción (en la forma demostrada), pero no si se basa en detectar un cambio en la entidad, como se puede hacer con la función anterior (preUpdateModifiedAt(PreUpdateEventArgs $args)).

Cuestiones relacionadas