Как обновлять другой документ в событиях Doctrine MongoDB ODM

В Doctrine MongoDB ODM другой документ в обработчике событий можно обновить через DocumentManager, но самый спокойный путь — вынести логику в сервис и обновлять оба документа в одном понятном сценарии. Внутри lifecycle-событий легко получить повторный flush, непредсказуемый порядок сохранения или изменение, которое Doctrine не увидит

Если задача простая, например после создания заказа обновить статистику пользователя, начните с сервисного метода:

public function createOrder(User $user, array $data): void
{
    $order = new Order($user, $data);
    $user->increaseOrdersCount();

    $this->dm->persist($order);
    $this->dm->persist($user);
    $this->dm->flush();
}

Так код проще читать: вы явно видите, что создается заказ и обновляется пользователь

Когда можно использовать событие

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

Пример подписчика:

use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs;

final class OrderSubscriber
{
    public function postPersist(LifecycleEventArgs $args): void
    {
        $document = $args->getDocument();

        if (!$document instanceof Order) {
            return;
        }

        $dm = $args->getDocumentManager();
        $user = $document->getUser();

        $user->increaseOrdersCount();
        $dm->persist($user);
    }
}

Важный момент: не вызывайте flush() внутри такого обработчика без крайней необходимости. Пусть текущий процесс сохранения завершится штатно

Если изменение не сохраняется

Если вы меняете документ в сложном событии вроде onFlush, Doctrine может не заметить новые изменения автоматически. Тогда нужно работать с UnitOfWork и пересчитать change set. Это уже более хрупкий сценарий, поэтому для большинства проектов лучше сервис или отдельная задача в очереди

Как проверить, что связанный документ обновился

После сохранения не ограничивайтесь тем, что код «не упал». Проверьте связанный документ отдельным чтением из базы:

$savedUser = $this->dm->getRepository(User::class)->find($user->getId());

if ($savedUser->getOrdersCount() !== 1) {
    throw new RuntimeException('Счетчик заказов не обновился');
}

В тесте полезно проверять не событие само по себе, а итоговую бизнес-операцию: создали заказ, сохранили, перечитали пользователя, увидели новое значение. Такой тест ближе к реальной задаче и быстрее показывает, где ошибка: в обработчике события, в сервисе, в маппинге документа или в условии, которое не сработало

Практичный выбор

Для бизнес-операций используйте сервис: создать заказ, обновить пользователя, сохранить все вместе

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

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

Если вы все же оставляете обновление внутри подписчика, держите обработчик маленьким: проверка типа документа, получение связанного объекта, одно понятное изменение. Не превращайте событие в место, где создаются новые документы, отправляются письма, запускаются HTTP-запросы и меняется несколько коллекций сразу. Чем больше побочных действий спрятано в событии, тем сложнее потом понять, почему MongoDB сохранила именно такое состояние

Частые ошибки

Вызывают flush внутри flush

Повторный flush() в обработчике может привести к странным эффектам. Если можно, избегайте этого

Прячут важную бизнес-логику в событии

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

Не проверяют тип документа

В обработчик могут попадать разные документы. Всегда проверяйте instanceof

Меняют документ, но Doctrine не видит изменение

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

Что почитать дальше по MongoDB

Если нужен общий маршрут по теме, откройте рубрику MongoDB. Для соседних задач пригодятся эти разборы:

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x