Comment vérifier si l'entité a changé dans la Doctrine 2?

je dois vérifier si une entité a changé et doit être mise à jour dans la base de données. Ce que j'ai fait (et cela n'a pas fonctionné) était le suivant:

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

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

ce code imprime toujours false, j'ai aussi essayé de tirer la chasse d'eau avant de vérifier l'Unité de travail, mais n'a pas fonctionné.

Quelqu'un a une suggestion?

23
demandé sur eagleoneraptor 2012-05-29 17:29:55

8 réponses

la première chose que je vérifierais que votre setName function est en train de faire quelque chose ($this-> nom = $nom... Si cela fonctionne déjà, vous pouvez définir un auditeur d'événements sur vos services.yml qui est déclenché lorsque vous appelez le flush.

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

alors vous définissez L'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 vous avez besoin de savoir quelles entités sont en train d'être modifiées, mais faites quelque chose avec elles après ils ont été sauvegardés dans la base de données, juste stockez les entités modifiées dans un tableau privé, puis définissez un événement flou qui récupère les entités du tableau.

BTW, pour déclencher ce genre d'événements vous devez ajouter les @ORM\HasLifecycleCallbacks sur l'entité.

22
répondu Sergi 2017-08-29 14:32:57

Vous pouvez également regarder les Pré-Update événement, si vous avez besoin d'accéder à l'entité champs avec leurs anciennes et nouvelles valeurs.

un petit exemple tiré principalement du lien fourni:

<?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');
            }
        }
    }
}
11
répondu Andrew Atkinson 2016-11-23 10:16:10

Je n'avais pas besoin/Je ne voulais pas créer D'Écouteurs pour mon cas donc j'ai fini avec

$product->setName('A different name');
$uow = $entityManager->getUnitOfWork();
$uow->computeChangeSets();
if ($uow->isEntityScheduled($product)) {
    // My entity has changed
}
10
répondu Pierre de LESPINAY 2015-03-06 15:55:21

Doctrine2 Docs. 17. Politiques De Suivi Des Changements

si vous utilisez le troisième formulaire (17.3. Informer) comme je le fais, vous pouvez tester si votre entité est changé en train de faire:

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

si rien n'a changé il retournera le tableau vide.

4
répondu Mikl 2018-07-25 10:38:27

la question est assez ancienne, mais il peut y avoir encore un groupe de personnes qui pourraient faire face à ce problème d'un point de vue différent. UnitOfWork fonctionne bien mais il retourne seulement le tableau des changements. Il peut être une douleur dans le cul quand quelqu'un ne sait pas réellement quels champs peuvent avoir changé et veut juste obtenir l'entity entière comme un objet à comparer $oldEntity et $newEntity. Même si l'événement est le nom de preUpdate si quelqu'un va essayer de récupérer les données depuis la base de données suit:

$er->find($id);

l'entité renvoyée contiendra tous les changements. La solution est assez simple, mais il a quelques crochets:

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

}

pour ceux qui utilisent un autre événement, comme preFlush, j'ai rapidement vérifié et la solution ne fonctionnait pas bien parce que probablement le refresh() la méthode écarte tout changement de couleur alors ce qui doit être fait est d'appeler la couleur une fois de plus dans l'écouteur et de créer un peu de statique $alreadyFlushed bascule pour éviter la référence circulaire.

2
répondu Mikołaj Król 2017-01-12 20:59:05

Je suis curieux de la Doctrine et de tous ceux qui documentent postFlush, comme dans certains cas, vous avez une transaction en cours. Je tiens à souligner qu'il ya aussi posttransaction commit, qui pourrait être plus sûr en fonction de ce que vous essayez d'atteindre dans l'événement postFlush.

0
répondu Tristan 2017-07-04 07:22:32

selon mes besoins, les réponses ici et la documentation, j'ai trouvé la solution suivante pour un modifiedAt horodatage D'une entité.

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

Ceci est basé sur ce que les docs disent sur ce Event contrairement aux autres, comme PostPersist et PreFlush:

PreUpdate est l'événement le plus restrictif à utiliser, puisqu'il est appelé juste avant qu'une déclaration de mise à jour ne soit appelée pour une entité à l'intérieur du Méthode EntityManager#flush (). Notez que cet événement n'est pas déclenché lorsque le jeu de modifications calculé est vide.

en utilisant PreUpdate contrairement aux autres, vous permet de laisser toutes les fonctions de calcul intensif au processus déjà défini par la Doctrine. Déclenchement manuel du calcul des changesets, comme dans ceux-ciréponses ci-dessus le serveur est intensif en CPU. L'événement onFlush, tel qu'utilisé dans la accepté de répondre à est une option (dans la voie démontré), mais pas si vous compter sur de la détection d'un changement de l'Entité, vous pouvez le faire avec la fonction ci-dessus (preUpdateModifiedAt(PreUpdateEventArgs $args)).

0
répondu rkeet 2017-11-14 13:00:57

je suis d'accord avec @Andrew Atkinson, quand il a dit:

Vous pouvez également regarder les PreUpdate événement, si vous avez besoin d' l'accès à l'entité champs avec leurs anciennes et nouvelles valeurs.

mais je ne suis pas d'accord avec l'exemple qu'il a proposé, d'après mon expérience, il y a une meilleure façon de vérifier si quelque chose a changé ou non.

<?php
class Spock
{
    public function preUpdate(PreUpdateEventArgs $eventArgs)
    {
        if (!empty($eventArgs->getEntityChangeSet())) {
            // fill this how you see fit
        }
    }
}

de cette façon le si ne sera déclenché que s'il y a vraiment un certain champ qui a changé ou pas.

comment faire si un champ a été modifié, alors oui, je recommande sa solution.

0
répondu Rafael 2018-08-06 07:01:56