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?
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é.
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');
}
}
}
}
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
}
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.
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.
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.
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)
).
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.