symfonydoctrine-ormdoctrine

Flushing in postPersist possible or not?


I have read the docs about lifecycle events, and several questions here on SO about changing or persisting new entities during lifecycle events. Calling EnitityManager::flush() seems to be a problem.

Ok, but looking carefully at the docs, there is a code example where the field is changed in postPersist, but no flush is called.

I checked that, and the suggested change is not written to the DB. Only the object being persisted does receive the change.

<?php

/** @Entity @HasLifecycleCallbacks */
class User
{
    // ...

    /**
     * @Column(type="string", length=255)
     */
    public $value;


    /** @PostPersist */
    public function doStuffOnPostPersist()
    {
        $this->value = 'changed from postPersist callback!';
    }
}

Maybe one should add this to the docs. I was mislead at first.

However, when adding the LifecyleEventArgs argument and flushing the contained EntityManager, they are written to DB:

/** @PostPersist */
public function doStuffOnPostPersist(LifecycleEventArgs $args)
{
    $this->value = 'changed from postPersist callback!';
    $args->getEntityManager()->flush(); // works in my tests. Is this safe to use ?

}

I don't know how to interpret the docs about whether it is OK or not to call flush inside the postPersist.

As you can see, I am searching for a reliable way to perform some kind of postprocessing to my entities after inserting or updating them. I have to use postPersist, since I need the auto-generated primary key value.

Side question: If yes, it is ok to flush, could I then also persist other objects in PostUpdate? Like so:

 /** @PostPersist */
public function doStuffOnPostPersist(LifecycleEventArgs $args)
{
    $this->value = 'changed from postPersist callback!';
    $obj = new OtherObject("value " . $this->value);
    $args->getEntityManager()->persist($obj);
    $args->getEntityManager()->flush(); // works in my tests. Is this safe to use ?

}

Side-side question: I have tried the last variant, and it seems to work. But is it efficient, or am I possibly creating deep recursion stacks? According to the docs, the postPersist code is called during flush, so if I call flush during postPersist, I have to be careful not to persist an object that executes the same handler, which would lead to infinite recursion. Is this correct?


Solution

  • I checked that, and the suggested change is not written to the DB. Only the object being persisted does receive the change.

    Maybe one should add this to the docs. I was mislead at first.

    The code in the docs doesn't try to persist this modification of the value property in database that's why no flush() is called. It's just show an example and this value could also be an unmapped to the database property of class User.

    I don't know how to interpret the docs about whether it is OK or not to call flush inside the postPersist.

    It is ok to call flush() on a PostPersist lifecycle callback in order to change a mapped property of your entity. In your PostPersist callback your entity has already been inserted in your database. By changing the property value and calling flush() your entity will be flag as to be updated, so the PostPersist event won't be dispatched again (instead Pre/PostUpdate events will be dispatched).

    Side question: If yes, it is ok to flush, could I then also persist other objects in PostUpdate?

    It is also ok to persist an new object of another entity class in a PostPersist event callback with no problem, but if you try to persist an object of the same (User) class in this PostPersist callback you will have an infinite recursion, as you can easily understand.

    Side-side question: I have tried the last variant, and it seems to work. But is it efficient, or am I possibly creating deep recursion stacks?

    As I have explained before this code doesn't create too deep recursion stacks or infinite loops if not persisting objects of the same class (User) in which the callback belongs. The flush() will be called exactly two times. Although things could get more complicated when having also to deal with associations, in your example there is not such problem.