Symfony 2 - флеш в postUpdate fire preUpdate event

Я обнаружил эту проблему "спасибо" исключению, которое я получил:

Catchable Fatal Error: Argument 3 passed to
Doctrine\ORM\Event\PreUpdateEventArgs::__construct() 
must be an array, null given, called in 
/.../vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php on line 804 
and defined in
/.../vendor/doctrine/lib/Doctrine/ORM/Event/PreUpdateEventArgs.php line 28

Я работаю над проектом, который требует определенной логики:
Когда поле order в объекте book изменено, мне нужно обновить поле books_order_modified_at в родительском объекте bookstore (это поле позволяет мне узнать, был ли изменен порядок книг в книжном магазине).

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

Я не нашел способа обновить связанный объект из события preUpdate, поэтому у меня есть приватное поле в классе слушателя, которое я использую, чтобы сообщить postUpdate событию обновить соответствующий объект bookstore.

Моя проблема в том, что когда я делаю так, событие preUpdate объекта book запускается.
Когда я проверяю набор изменений, он содержит только поле modified_at, но имеет то же значение до и после.

Если у кого-то есть другая идея, как решить проблему - отлично.

Если нет - любая идея, как я могу предотвратить событие preUpdate от запуска, когда флеш вызывается в теге postUpdate event??

Ответ 1

На самом деле это проблема из доктрины Doctrine Issue DDC-2726. Решив его, добавив четкий вызов менеджера сущностей после флеша в слушателе, так что 3-й аргумент этого конструктора, который на самом деле является entityChangeSets, будет переписан.

Ответ 2

Как насчет обновления modified_at внутри ваших объектов и позволить доктрине справиться с этим? Вы изменили бы метод setOrder в своей книге, чтобы обновить объект BookOrder следующим образом:

class Book {
    public function setOrder($order) {
        // modify book
        $this->bookOrder->updateModifiedAt();
    }
}

Конечно, вашему BookOrder пришлось бы реализовать modifiedAt:

class BookOrder {
    public function updateModifiedAt() {
        $this->modifiedAt = new \DateTime();
    }
}

Если вы используете другие классы для своего Datetime, вы, конечно, должны изменить этот код!

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

Ответ 3

Я могу предложить вам использовать Timestampable extension для Doctrine из DoctrineExtensionsBundle.

Используя его, вам не нужно устанавливать значения created_at или modified_at. Это расширение делает это автоматически. Даже он может устанавливать modified_at только при изменении определенных полей. См. пример.

Я думаю, вы пишете что-то вроде этого расширения. Итак, вам не нужно это делать, потому что это уже сделано:)

Ответ 4

У меня была аналогичная проблема. Попытка использовать preupdate для изменения дочерних элементов вызвала ту же ошибку. В конце концов, мое решение просто обновить дочерние элементы родителя. Явный запрос на flush не требуется.

/**
 * Update expiry dates for all runners belonging to a campaign
 *
 * @param $runners
 * @param $expiryDate
 */
private function updateCampaignRunners($runners, $expiryDate){
    foreach($runners as $runner){
        $runner->setExpiresAt($expiryDate);
        $this->getModelManager()->update($runner);
    }
}

/**
 * Post update and persist lifecycle callback
 *
 * @param Campaign $campaign
 */
private function postAction(Campaign $campaign)
{
    $runnerExpire = $this->getForm()->get("runnerExpire")->getData();
    if($runnerExpiryDate && $campaign->getRunners()){
        $this->updateCampaignRunners($campaign->getRunners(), $runnersExpiryDate);
    }
}