Как проверить, изменилась ли сущность в Doctrine 2?

Мне нужно проверить, изменился ли постоянный объект и его необходимо обновить в базе данных. То, что я сделал (и не работало), было следующим:

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

Этот код печатает всегда false , я также попытался выполнить флеш перед проверкой единицы работы, но не работал.

У кого-нибудь есть предложение?

Первое, что я хотел бы проверить, что ваша функция setName на самом деле что-то делает ($ this-> name = $ name …). Если он уже работает, вы можете определить прослушиватель событий на вашем service.yml, который запускается, когда вы называете флеш.

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

Затем вы определяете 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 } } } 

Если вам нужно знать, какие объекты изменяются, но что-то с ними делать после того, как они были сохранены в базе данных, просто сохраните сущности, измененные в приватном массиве, а затем определите событие onFlush, которое получает сущности из массива.

BTW, чтобы инициировать подобные события, вам нужно добавить @ORM \ HasLifecycleCallbacks на объект.

Вы также можете посмотреть событие PreUpdate , если вам нужен доступ к полям объекта со старыми и новыми значениями.

Немного примера, взятого из приведенной ссылки:

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

Мне не нужно было / хотеть создавать Listeners для моего дела, поэтому я закончил с

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

Doctrine2 Docs. 17. Изменение правил отслеживания

Если вы используете третью форму (17.3. Уведомлять), как я, вы можете проверить, изменилось ли ваше лицо:

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

Если ничего не изменится, он вернет пустой массив.

Проблема довольно старая, но может быть еще какая-то группа людей, которые могут столкнуться с этой проблемой с другой точки зрения. UnitOfWork работает, но возвращает массив изменений. Это может быть болью в прикладе, когда кто-то на самом деле не знает, какие поля могут быть изменены, и просто хочет, чтобы весь объект был объектом сравнения $oldEntity и $newEntity . Даже если имя события является preUpdate, если кто-то попытается извлечь данные из базы данных следующим образом:

 $er->find($id); 

возвращенный объект будет содержать все изменения. Обходной путь довольно прост, но у него есть некоторые крючки:

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

Для тех, кто использует другое событие, например preFlush, я быстро проверил его, и обходной путь не сработал, потому что, вероятно, метод refresh() отбрасывает любые изменения флеша, так что нужно сделать, чтобы снова вызвать флеш в прослушиватель и создать некоторый статический $alreadyFlushed чтобы избежать циклической ссылки.

Мне интересно, как Doctrine и все документируют postFlush, так как в некоторых случаях у вас есть текущая транзакция. Я хотел бы указать, что есть также postTransactionCommit, который может быть более безопасным в зависимости от того, чего вы пытаетесь достичь в событии postFlush.

Основываясь на моих потребностях, ответах здесь и документах , я придумал следующее решение для timestamp modifiedAt в Entity.

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

Это основано на том, что говорят документы об этом Event в отличие от других доступных, таких как PostPersist и PreFlush :

PreUpdate является наиболее ограничивающим для использования событием, так как он вызывается непосредственно перед вызовом update для сущности внутри метода EntityManager # flush (). Обратите внимание, что это событие не запускается, когда вычисленный набор изменений пуст.

Использование PreUpdate в отличие от других, позволяет оставить все вычисления и вычислить интенсивные функции процессу, уже определенному Doctrine. Ручное инициирование вычислений наборов изменений, например, в приведенных выше ответах, является интенсивным сервером. Событие onFlush, например, используемое в принятом ответе, является опцией (продемонстрированным способом), но если вы не полагаетесь на обнаружение изменения в Entity, как это возможно, с помощью функции выше ( preUpdateModifiedAt(PreUpdateEventArgs $args) ).