Doctrine update event on loaded Entity instance - symfony

I have problem with event in Doctrine , i need event on persist object that is already loaded
$obj = $em->getRepository(EntityName::class)->findById($someId);
$obj2 = clone $obj;
$obj2->property = 'some value';
$obj->property = 'some diff value';
$em->persist($obj2);
$em->persist($obj);
I register event in EntityName class on event #PrePersist and that works perfect for $obj2 but event is not fired for $obj, is there way to delete $obj from $em->UnitOfWorks and fire event for $obj.
The trick is that i must fire event before $em->flush()

#PrePersist only triggers when you are inserting a new entity. If you've already found an existing entity like you have above, then you will need to use the #PreUpdate event.
The Doctrine documentation contains information for all these events.
Of course, without knowing what you are attempting to do, and seeing your #PrePersist code, it's impossible to help you further. You might be able to use onFlush like Cerad suggested. You may need both prePersist and preUpdate.
Either way, merely calling $em->persist() does not guarantee that the prePersist event is going to be triggered.

Related

need explanation on postPersist and data flushed

i had trouble with an entity who was not flushed correctly.
In a service, i setted somme values. before flushing them in the service, i call another service and i saw there was a listener linked.
In this listener, there was a postPersist method in witch was called "$entityManager->flush();"
It was the source of my problem.
I found this post : Doctrine inserting in postPersist event
So, i deleted the flush who was done in the postPersist.
But i don't understand the need of the method postFlush.
In my case, data is flushed even if i don't have this method. how is it possible that the properties setted in the listener are flushed correctly without this event ?
If i look other entry points, i see that i need to declare the postFlush event and i see the need of this method.
thanks for your help
No, you don't need to flush in a postPersist event, because it will be done soon, just after Persist. You don't need to use all the functions of the list, neither declaring them.
ps.: Only if you need to get/set data before persist/flush. You would need to compute changes then get them in the action 'couple' (e.g prePersist and postPersist, preUpdate and postUpdate).
docs says:
Changes to fields of the passed entities are not recognized by the
flush operation anymore, use the computed change-set passed to the
event to modify primitive field values.
and
getEntityChangeSet() to get a copy of the changeset array. Changes to
this returned array do not affect updating.
PostFlush
The postFlush is made at the end of a flush operation. According to the docs this event is not a lifecycle callback. You can use it to set something after registering, or even send notifications, clearly with postFlush you won't need to worry about lifeCycle events.
postFlush - The postFlush event occurs at the end of a flush
operation. This event is not a lifecycle callback.
For postPersist in the docs
postPersist - The postPersist event occurs for an entity after the
entity has been made persistent. It will be invoked after the database
insert operations. Generated primary key values are available in the
postPersist event.
Here you can have ids if you need before flush.
You can check the docs about LifeCycleEvents here:
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events

Trigger Doctrine lifecycle events in my code

Question: how to trigger Doctrine lifecycle events in my code, having the entity data available?
Details
I have an active listener on the Doctrine postPersist and postUpdate events.
I cannot modify / override this listener.
In some places in my code, for performance reasons, I use DBAL to save data instead of ORM methods.
I'd like to stick to the same event system.
Thank you in advance for your help.
Extending #Cerad answer, here's a very basic sample code to achieve the result (trigger a Doctrine LifeCycle event). This sample assumes we're in a Symfony controller:
use Doctrine\ORM\Event\LifecycleEventArgs;
// ...
$user = new AppBundle\Entity\User();
// ... do something with the user
$entityManager = $this->getDoctrine()->getManager();
$eventManager = $entityManager->getEventManager();
$eventArgs = new LifecycleEventArgs($user, $entityManager);
$eventManager->dispatchEvent(\Doctrine\ORM\Events::postPersist, $eventArgs);
Assuming you have access to the entity manager then:
$eventManager = $entityManager->getEventManager();
After that you can build and dispatch events per the documentation.
I checked to see if there is a predefined service but did not see one. But you probably define one as a factory and inject it as needed.
Hope this is what you are asking for.

How do I check that a particular lifecycle event method runs in a Doctrine event subscriber?

I have a preUpdate event hook in an event listener and I want to write a test that just verifies that it gets executed when a particular document is updated. The only documentation I've been able to find about testing anything Doctrine-related was about testing query builders. I am new to Doctrine and this seems like a very simple thing to do but I really can't figure it out.
You can use partial mock. Lets say the class that has preUpdate hook is called Entity.
$mock = $this->getMockBuilder('Mock')->setMethods(array('preUpdate'))->getMock();
This create an object for which only the preUpdate method is stubbed and you can write expectations to this method:
$mock->expects($this->once())->method('preUpdate');
// some code that triggers the hook
All other methods of the class will work as in the original class - they would not be overriden

Preserving changes made to a document with Doctrine's PreUpdate lifecycle event

I have an event subscriber that contains two lifecycle event methods: prePersist and preUpdate. The prePersist one is working as expected - I modify the document, and the changes are reflected later when I retrieve the document. preUpdate is not working as expected, though. Here's basically what it looks like:
/**
* Also update the foo code when the bar profile is updated.
* #param LifecycleEventArgs $args
*/
public function preUpdate(LifecycleEventArgs $args)
{
$document = $args->getDocument();
if ($document instanceof BarProfile) {
$document->setFooCode('Salamanders');
}
}
If I create a document and set its fooCode to 'placeholder' in the perPersist function, then when I retrieve the document later it fooCode is 'placeholder'. If I then update it, and retrieve it again, then I expect its fooCode to be 'Salamanders'. However, it's still 'placeholder'. I've even tried putting error_log() stuff in there and it writes stuff to the logs so I can see that this method is being executed.
Is there a second step I have to do after $document->setFooCode() to make the new value of fooCode stick around?
You cannot modify the fields directly in the preUpdate event, you have to modify their primitive values. Changes to associations are not allowed. You would have to do:
$eventArgs->setNewValue('fooCode', 'Salamanders');
You stated "The prePersist one is working as expected - I modify the document, and the changes are reflected later when I retrieve the document."
This leads me to believe that you may not be aware of the difference between persist and update. In Doctrine, a persist occurs when you are creating the object for the first time. An update occurs when you are making changes to an existing object that is already being managed by Doctrine. A lot of people are confused by this, but you do not need to call persist() when you are updating an existing entity, you only need to call flush(). For example:
// inserts a new entity into the database
$document = new Document();
$document->setName('My Document');
$em->persist($document);
$em->flush();
and
// retrieves entity from the database, makes a change, then updates the database
$document = $em->findOneByName('My Document');
$document->setFooCode('Salamanders');
$em->flush();
I encourage you to read the Doctrine documentation like Cerad suggested. Play close attention to the following statements for the preUpdate event:
PreUpdate is the most restrictive to use event
Changes to associations of the updated entity are never allowed in this event
Changes to fields of the passed entities are not recognized by the flush operation anymore, use the computed change-set passed to the event to modify primitive field values

removing entities in preUpdate event lister

I have a preUpdate listener for an Entity where I do some calculations, set values etc.
In that listener I would like to remove some other related entities, but this does not seem to be flushed by the entity manager. How can I go about achieving this?
According to the Doctrine2 docs:
Changes to associations of the updated entity are never allowed in
this event, since Doctrine cannot guarantee to correctly handle
referential integrity at this point of the flush operation.
Which means you shouldn't mess with the entities during the preUpdate event handling. I suggest you move your logic up to the service layer by using an entity manager. Write a specific method for updating your entitty and do all the complex stuff there. A nice example of an entity manager would be the FOSUserBundle's UserManager
If you make a change to an entity in a preUpdate event listener you need to tell the unit of work to recompute that entity's changeset:
$em = $eventArgs->getEntityManager();
$cm = $em->getClassMetadata(get_class($entity));
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);

Resources