События в Doctrine2

Речь пойдет о событиях которые можно лицезреть в классе Doctrine\ORM\Events.

prePersist

Возникает при:

  • $entityManager->persist($entity)
  • $entityManager->flush() для новых сущностей в связях при cascade= {"persist"}
  • $entityManager->merge($entity)
  • $unitOfWork->computeChangeSet($classMetadata, $entity) для новых сущностей в связях при cascade={"persist"}

Нюансы:

  • Возникает для каждой сущности отдельно (нет доступа к методам $uow->getScheduled...() )
  • На этом этапе в сущности нет идентификатора
  • Возникает для связей c cascade={"persist"}
  • Изменения связей в подписчике не учитываются

preRemove

Возникает при:

  • $entityManager->remove($entity)

Нюансы:

  • возникает для связей с cascade={"remove"}
  • Изменения полей сущности в подписчике не учитываются
  • если не указать cascade={"remove"}, то событие не возникает, даже если указано @ORM\JoinColumn(onDelete="CASCADE") ведь ответственность ложится на базу, а не на доктрину

preFlush

Возникает при:

  • $entityManager->flush()
  • $unitOfWork->computeChangeSet($classMetadata, $entity)

Нюансы:

  • При возникновении этого события можно безопасно вызывать $entityManager- >flush() без ограничений
  • Если в связях используется cascade= {"persist"}, то методы $uow->getScheduled...() еще не знают про автоматический persist для связей (читай методы $uow->getScheduled...() не знают, что к заперсистенному объекту добавлены другие объекты в результате которых будет выполнен INSERT в связанные таблицы)

onFlush

Возникает при:

  • $entityManager->flush() после вычислений changeSet НО перед стартом транзакции

Имеет доступ ко всем изменениям:

  • foreach ($uow->getScheduledEntityInsertions() as $entity) {}
  • foreach ($uow->getScheduledEntityUpdates() as $entity) {}
  • foreach ($uow->getScheduledEntityDeletions() as $entity) {}
  • foreach ($uow->getScheduledCollectionDeletions() as $collection) {}
  • foreach ($uow->getScheduledCollectionUpdates() as $collection) {

Нюансы:

  • Создавать сущности можно только пересчитав их changeSet:
    $unitOfWork->computeChangeSet($classMetadata, $entity)
  • Изменения в связанных сущностях нужно пересчитывать:
    $unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)
  • Событие возникает даже, если changeSet пустой

postFlush

Возникает:

  • внутри вызова $entityManager->flush() - после коммита транзакции

Нюансы:

  • Событие возникает даже, если changeSet пустой
  • В подписчике этого события не рекомендовано вызывать $entityManager->flush() (если это сделать, то придется вручную бороться с цикличностью)
  • Чтобы отследить данное событие, нужно отдельно зарегистрировать слушателя:
    $em->getEventManager()->addEventListener([\Doctrine\ORM\Events::postFlush], new MyListener());

preUpdate

Возникает:

  • Перед обновлением сущности в $entityManager->flush()

Нюансы:

  • Событие возникает если changeSet пустой
  • Пересчитывать chageSet не нужно - можно менять любые сущности не вызывая:
    $unitOfWork->computeChangeSet($classMetadata, $entity)
    $unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity)
  • Однако, не разрешается менять скаляры напрямую, только с помощью:
    Doctrine\ORM\Event\PreUpdateEventArgs $args->setNewValue('field', 'value')
    (еще раз: изменения скаляров напрямую будет проигнорировано)
  • На этом этапе не рекомендуется вызывать $entityManager->persist($entity) или $entityManager->remove($entity) даже с использованием $unitOfWork может возникнуть unexpected behavior
  • Не разрешается менять связанные сущности (потому что для них будет вызван свой Event)
use Doctrine\ORM\Event\PreUpdateEventArgs;

public function preUpdate(PreUpdateEventArgs $event)
{
    $entity = $event->getEntity();
    $changeSet = $event->getEntityChangeSet();

    // Проверяем, изменялось ли конкретное поле "someField"
    if (isset($changeSet['someField'])) {
        $oldValue = $changeSet['someField'][0];
        $newValue = $changeSet['someField'][1];
        // Действия при изменении поля
    }

    // Или можно перебрать все изменённые поля
    foreach ($changeSet as $field => $values) {
        $oldValue = $values[0];
        $newValue = $values[1];
        // Обработка изменений
    }
}

postPersist, postUpdate, postRemove

Возникает:

  • После соответствующих действий

Нюансы:

  • Изменения в сущностях не учитываются
  • postPersist - возникает после выполнения инструкции INSERT INTO ... но до коммита транзакции
  • postUpdate - возникает после выполнения инструкции UPDATE ... но до коммита транзакции
  • postRemove - возникает после выполнения инструкции DELETE FROM ... но до коммита транзакции. Если не указать cascade={"remove"}, то событие не возникает, даже если указано @ORM\JoinColumn(onDelete="CASCADE") ведь ответственность ложится на базу, а не на доктрину

onClear

Возникает:

  • При вызове $entityManager- >clear()
  • После $entityManager- >flush()

Пример использования: чистить кастомные кэши.

postLoad

Возникает:

  • При создании новой сущности после гидрации (когда из бд пришла строчка, которая превращается в сущность)
  • При очистке гидратора

loadClassMetadata

Возникает:

  • После чтения маппингов и создании Doctrine\ORM\Mapping\ClassMetadataInfo на их основании

Пример использования: можно добавить динамические поля (магические поля) и метаданные будут работать

onClassMetadataNotFound

Возникает:

  • Если данные для сущности не найдены, можно добавить fallback

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

Источник: 1


15.02.2011 08:48