Doctrine Transaction and EventSubscriber #6203

Closed
opened 2026-01-22 15:28:51 +01:00 by admin · 4 comments
Owner

Originally created by @NicolaPez on GitHub (Mar 20, 2019).

I'm writing here a support question because I don't find the solution around the web, sorry.

I'm writing a method in my Symfony 3 application for bulk user creation. The flux is uploading a csv file with all the necessary data.

I created a Service, into I write all the logic of this operation. This is my Service:

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;
    }
}

Also I wrote this EmailSubscriber, for sending an activation email each time the entity User is persisted:

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);
        }
    }
}

And this is question:

The EventSubscriber catch the persisted event before the transaction commit. I want or persist all the row in my db, or response with violation and ask to User to modify his csv file.

Because this, one of the useCase can be some activation email sended but no persisting the User in DB, for example for some validate violation of one of the csv row.

I hope I was crearl, the case is a bit intricate.

Originally created by @NicolaPez on GitHub (Mar 20, 2019). I'm writing here a support question because I don't find the solution around the web, sorry. I'm writing a method in my Symfony 3 application for bulk user creation. The flux is uploading a csv file with all the necessary data. I created a Service, into I write all the logic of this operation. This is my Service: ``` 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; } } ``` Also I wrote this EmailSubscriber, for sending an activation email each time the entity User is persisted: ``` 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); } } } ``` And this is question: The EventSubscriber catch the persisted event before the transaction commit. I want or persist all the row in my db, or response with violation and ask to User to modify his csv file. Because this, one of the useCase can be some activation email sended but no persisting the User in DB, for example for some validate violation of one of the csv row. I hope I was crearl, the case is a bit intricate.
admin closed this issue 2026-01-22 15:28:52 +01:00
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

You probably want to hook into postFlush instead - that's when you know that the data was saved successfully.

@Ocramius commented on GitHub (Mar 20, 2019): You probably want to hook into [`postFlush`](https://github.com/doctrine/orm/blob/434820973cadf2da2d66e7184be370084cc32ca8/lib/Doctrine/ORM/Events.php#L158) instead - that's when you know that the data was saved successfully.
Author
Owner

@NicolaPez commented on GitHub (Mar 20, 2019):

But with that Event I cant get each entity flushed, as lifecycle event permit, right?
So I don't know how resolve this issue.

@NicolaPez commented on GitHub (Mar 20, 2019): But with that Event I cant get each entity flushed, as lifecycle event permit, right? So I don't know how resolve this issue.
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

You'd collect the entities in onFlush, then operate during postFlush.

I don't think you should perform this sort of operations when tied to the ORM though, as it is hiding your business logic very deep in the EntityManager integration, which will be hard to understand/debug for future maintainers of your code (including yourself).

@Ocramius commented on GitHub (Mar 20, 2019): You'd collect the entities in `onFlush`, then operate during `postFlush`. I don't think you should perform this sort of operations when tied to the ORM though, as it is hiding your business logic very deep in the EntityManager integration, which will be hard to understand/debug for future maintainers of your code (including yourself).
Author
Owner

@NicolaPez commented on GitHub (Mar 20, 2019):

Ok, I'll try to think about this and improve my flux.
Your advice was useful. Thanks.

@NicolaPez commented on GitHub (Mar 20, 2019): Ok, I'll try to think about this and improve my flux. Your advice was useful. Thanks.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6203