Мне нужно проверить, изменился ли постоянный объект и его необходимо обновить в базе данных. То, что я сделал (и не работало), было следующим:
$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)
).