Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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 на объект.

Ответ 2

Вы также можете посмотреть событие 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');
            }
        }
    }
}

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

Проблема довольно старая, но может быть еще какая-то группа людей, которые могут столкнуться с этой проблемой с другой точки зрения. 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, чтобы избежать циклической ссылки.

Ответ 6

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

Ответ 7

В соответствии с моими потребностями, ответы здесь и документы, я придумал следующее решение для 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;
}

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

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

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