Объект, созданный в PostRemove Listener, не сохраняется - PullRequest
0 голосов
/ 02 мая 2018

Я работаю над проектом на базе Symfony 2.8, который в основном представляет собой онлайновую адресную книгу.

EntityDeletionListener используется для обработки postRemove событий, чтобы добавить некоторую информацию в журнал удалений, которая необходима для некоторого внутреннего учета:

class EntityDeletionListener implements EventSubscriber {
    public function getSubscribedEvents() {
        return array('postRemove');
    }

    public function postRemove(LifecycleEventArgs $args) {
        $entity = $args->getEntity();

        if (!$entity->shouldAddToDeleteLog())
           return ;

        $em = $args->getEntityManager();
        $repo = $em->getRepository('AppBundle:DeleteLog');

        $log = $repo->createEntity();
        $log->setGuid($entity->getGuid());
        ...

        $em->persist($log);

        // Flush entity manager if not disabled in entity settings
        if ($entity->shouldFlushDeleteLog())
            $em->flush();
    }
}

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

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

class ContactRepository extends EntityRepository {
    public function bulkDelete($guids) {
        $this->_em->getConnection()->beginTransaction();

        try {
            $batchSize = 100;
            $currentBatch = 0;           

            foreach ($guids as $guid) {
                $contact = $this->findOneByGuid($guid);

                if ($contact) {        
                    // DO NOT FLUSH EVERY DELETE LOG
                    $contact->setDeleteLogFlush(false);

                    $this->_em->remove($contact);

                    $currentBatch++;
                    if ($currentBatch % $batchSize === 0)
                        $this->_em->flush();
                }
            }

            $this->_em->flush();  
            $this->_em->getConnection()->commit();
        } catch (\Exception $ex) {            
            $this->_em->getConnection()->rollBack();
            return false;
        }
    }
}

Задача 1: Если я использую $contact->setDeleteLogFlush(false), чтобы избежать автоматической очистки каждого DeleteLog автоматически, DeleteLogs не сохраняется вообще. Также сущности Contact удаляются корректно, и postRemove выполняется для каждой операции удаления, журналы не сохраняются в БД.

Нет исключений или других ошибок, которые объясняли бы, почему журналы не сохраняются. Даже если будет ошибка, в этом случае полная транзакция не будет выполнена, и Contacts также не будет удален. Но они удалены ...

Задача 2: Если НЕ использовать $contact->setDeleteLogFlush(false), прослушиватель postRemove сбрасывает каждую новую DeleteLog, я получаю следующее исключение, как только несколько записей удаляются одновременно:

Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalThrowableError: "Type error: Argument 2 passed to Doctrine\DBAL\Connection::delete() must be of the type array, null given

Как это решить? Почему журналы, созданные в приемнике postRemove, не сохраняются при очистке менеджера сущностей?

1 Ответ

0 голосов
/ 02 мая 2018

Мне кажется, проблема в том, что вы хотите создать и удалить несколько Contact сущностей, но вы делаете это в неправильном событии, потому что, как указано в документации

Событие postRemove наступает для объекта после того, как объект был удален. Он будет вызван после операций удаления базы данных. Это не вызывается для оператора DQL DELETE.

и вам не следует вносить какие-либо изменения в базу данных, как указано здесь

postUpdate, postRemove, postPersist

Три события post вызываются внутри EntityManager # flush (). изменения здесь не имеют отношения к постоянству в базе данных, но вы можно использовать эти события для изменения непостоянных элементов, например, не отображаемых поля, ведение журнала или даже связанные классы, которые не являются непосредственно составлен доктриной.

Так что я бы просто переместил всю логику postRemove в preRemove событие, начиная с

Событие preRemove вызывается для каждого объекта, когда оно передается EntityManager # метод remove (). Это каскадно для всех ассоциаций помеченные как каскадное удаление.

...