Symfony3: транзакция Doctrine и подписчик событий

#symfony #doctrine #event-listener

#symfony #доктрина #прослушиватель событий

Вопрос:

Я пишу метод в своем приложении Symfony 3 для массового создания пользователей. Поток загружает CSV-файл со всеми необходимыми данными.

Я создал сервис, в который я записываю всю логику этой операции. Это мой сервис:

 class BulkRegistration
{
    private $em;
    private $validator;
    private $session;

    public function __construct(EntityManagerInterface $em, ValidatorInterface $validator, SessionInterface $session)
    {
        $this->em = $em;
        $this->validator = $validator;
        $this->session = $session;
    }

    public function run(BulkRegistrationData $bulkRegistrationData){
        //todo rimuovere dipendenza nascosta
        $serializer = new Serializer([new ObjectNormalizer()], [new CsvEncoder()]);

        $datas = $serializer->decode(file_get_contents($bulkRegistrationData->csv), 'csv');

        $this->em->getConnection()->beginTransaction();

        try{
            foreach($datas as $data)
            {
                $userData = UserData::create($data);
                $this->validate($userData, 'newUser');

                $userCreate = User::create($userData->user);
                $this->em->persist($userCreate);


                $this->em->flush();
            }

            $this->em->getConnection()->commit();

        } catch (Exception $e) {
            $this->em->getConnection()->rollback();
            $this->em->close();

            $this->session->getFlashBag()->add('error', $e->getMessage());

            return false;
        }

        return true;
    }

    private function validate ($entity, $validationGroup = null){
        if($validationGroup){
            $errors = $this->validator->validate($entity, null, [$validationGroup]);
        }else{
            $errors = $this->validator->validate($entity);
        }

        if (count($errors) > 0) {
            $errorMessage = '';
            foreach($errors as $error)
            {
                $errorMessage .= $error->getMessage();
            }
            throw new Exception($errorMessage);
        }

        return;
    }
}
  

Также я написал этот EmailSubscriber для отправки электронного письма с активацией каждый раз, когда сохраняется пользователь объекта:

 class EmailSubscriber implements EventSubscriber
{
    private $activationEmail;

    public function __construct(SendActivationEmail $activationEmail)
    {
        $this->activationEmail = $activationEmail;
    }

    public function getSubscribedEvents()
    {
        return array(
            Events::postPersist,
        );
    }

    public function postPersist(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();
        $entityManager = $args->getObjectManager();

        if ($entity instanceof User)
        {
            $this->activationEmail->send($entity);
        }
    }
}
  

И это вопрос:

Подписчик событий перехватит сохраненное событие до фиксации транзакции. Я хочу либо сохранить всю строку в моей базе данных, либо ответить с нарушением и попросить пользователя изменить его CSV-файл.

Из-за этого одним из вариантов использования может быть отправленное электронное письмо с активацией, но пользователь не сохраняется в базе данных, например, для проверки нарушения одной из строк csv.

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

Ответ №1:

Я думаю, вам нужно настроить foreach на сброс только в том случае, если ошибок не было:

 foreach($datas as $data) {
    $userData = UserData::create($data);
    try {
        $this->validate($userData, 'newUser');
    } catch (Exception $e) {
        $this->em->getConnection()->rollback();
        $this->em->close();

        $this->session->getFlashBag()->add('error', $validation);

        return false;
    }
    $userCreate = User::create($userData->user);
    $this->em->persist($userCreate);
}
$this->em->flush();
$this->em->getConnection()->commit();

return true;
  

Комментарии:

1. Но если я не выполняю сброс в любое время, некоторая проверка пользовательских данных (например, uniqueEmail) не может быть перехвачена, поэтому у меня исключение sql, а не нарушение формы.

2. Затем вам нужно проверить уникальные поля отдельно, возможно, с помощью array_count_values().

3. Да, это может быть решением. Но я решил изменить свой способ, сделать что-то менее сложное. Я заполняю и использую массив и отправляю электронную почту вручную. Спасибо за помощь.