Создание/сохранение нового документа во время события preUpdate в доктрине-mongodb

Я использую doctrine-mongodb-odm-1.0.0-BETA10 и пытаюсь предоставить некоторую пользовательскую логику на основе \InitialDocument во время выполнения события preUpdate.

Допустим, \InitialDocument получил некоторое состояние, которое должно вести себя как начальное для нового \StateDocument. Я делаю что-то вроде этого:

class InitDocListener implements \Doctrine\Common\EventSubscriber {
    public function getSubscribedEvents()
    {
        return [
            Events::preUpdate
        ];
    }

    public function preUpdate($args){
        $document = $args->getDocument();
        if($document instanceOf InitialDocument && $document->getState() == 'mine'){
            $stateDocument = new \StateDocument();
            $stateDocument->setInitDocument($document);
            $args->getDocumentManager()->persist($stateDocument);
            //no flush cause recursion happens
        }
    }

}

prePersist событие \StateDocument происходит, но новый документ не сохраняется в БД. и событие postPersist соответственно никогда не будет запущено.

Есть еще некоторая пользовательская логика, но все в области событий. В какой-то момент эта логика может вызвать исключение, которое должно остановить событие обновления InitialDocument, поэтому состояние InitialDocument зависит от процесса создания \StateDocument в бизнес-сфере.

Как я могу решить эту проблему? preFlush событие, выполняемое перед пересчетом changeSet, не определяет экземпляр InitialDocument. Так что это своего рода уловка «искать» обновления в preFlush и заставляет меня думать, что это неправильный способ. Пожалуйста, посоветуйте мне правильный. Спасибо.


person lazycommit    schedule 29.05.2014    source источник


Ответы (1)


Я создал тестовый пример для вашего варианта использования здесь. Одна вещь, которая выделялась из кода в вашем вопросе, заключалась в том, что вы не вызывали recomputeSingleDocumentChangeSet() для документа, который вы изменяли во время обратного вызова жизненного цикла, как упоминается в preUpdateдокументация. Но даже при этом вызове новый документ не будет вставлен. Это связано с тем, что UnitOfWork выполняет обновления после вставок и обновлений. Полный порядок можно увидеть в Метод UnitOfWork commit():

  • Вставки документов
  • Вкладыши для документов
  • Обновления документов
  • Дополнительные обновления (запланированные внутренними классами-постоянниками)
  • Удаление коллекции
  • Обновления коллекции
  • Удаление документов

Когда отправляется событие preUpdate, вставки/вставки для новых документов уже произошли. Даже при вызове recomputeSingleDocumentChangeSet() вы в конечном итоге планируете вставку документа, но UnitOfWork игнорирует это и в конечном итоге отменяет его при очистке всех очередей расписания здесь.

Хотя простым решением для ODM была бы проверка дополнительных вставок после обработки обновлений, в некоторых ситуациях это может привести к бесконечному циклу. Заказ UnitOfWork предшествовал моей работе над проектом, но риск зацикливания мог быть проблемой, когда задумывалась первоначальная реализация.

В качестве обходного пути вы можете захотеть, чтобы прослушиватель выгрузил новые документы, которые будут вставлены в какой-либо другой контейнер (или сам прослушиватель), а затем проверить постфактум на наличие дополнительных документов, которые будут сохранены/сброшены.

person jmikola    schedule 29.05.2014
comment
Спасибо. Вчера я немного поигрался с UnitOfWork и пришел к аналогичным выводам. Вы только что подтвердили предположение, что в этом случае нам нужно обрабатывать документы вставки в каком-то другом контейнере. Проблема в том, что preUpdate событие вызывает состояние MANAGED для нового документа, сохраняющееся в то время и после фиксации //Clear up его вставок (и прочего). Документ остался известным и указан как УПРАВЛЯЕМЫЙ, но это вызывает ошибки во время нового сохранения/сброса этого документа. - person lazycommit; 30.05.2014
comment
Поэтому я попытался обернуть flush() для обработки собственного пользовательского события afterCommit (после clear up) и снова добавить scheduleForUpsert, чтобы предотвратить выполнение persist(). Но это не сработало. Теперь я пытаюсь полностью отказаться от preUdpate в пользу своего afterCommit. Будет проведено несколько (отказоустойчивых?) тестов, если проблема будет одобрена для публикации на GitHub. - person lazycommit; 30.05.2014
comment
После всего. Решено отказаться от события preUpdate в пользу обернутого сброса и реализовать событие afterCommit с поведением отката, если новая логика приложения сохраняемости документов выдает сбои. Очередь: 1. preUpdate проверяет исходные документы и вносит изменения в набор для откатов. 2.afterUpdate прилагает документы к updated_docs collection. 3 Событие afterCommit инициирует создание новых документов на основе updated_docs_collection и их сброс или откат обновленных документов в случае сбоя. Не очень хорошая реализация, но пока работает. - person lazycommit; 30.05.2014