I have entity answers and I use softdeleted filter for them, and when I remove entity in some action everything fine, I have deletedAt datetime, but when I try remove this entity in OnFlushEvent my entity is gone from DB, why ?
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$em->getFilters()->enable('softdeleteable');
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof Questions) {
$existAnswers = $this->container->get('app.repository.question_answers')
->findOneBy(['questions' => $entity]);
$em->remove($existAnswers);
}
}
}
entity
* #Gedmo\SoftDeleteable(fieldName="deletedAt")
*/
class QuestionAnswers
config service
app.doctrine_listener:
class: AppBundle\Listener\DoctrineListener
calls:
- [setContainer, ['#service_container']]
tags:
- { name: doctrine.event_subscriber, connection: default }
I checked, this filter enabled, I try force enable but no helped
I think the issue might be related to subscribers priority.
In fact seems that also SoftDeleteableListener implements a subscriber which collect entities to softdelete using onFlush() event as we can see here. Therefore if your event is fired after the softdeletable one, your entities will be normally deleted from Doctrine.
To avoid this I would set priotity on your subscriber in order to fire your events before SoftDeleteableListener ones
app.doctrine_listener:
class: AppBundle\Listener\DoctrineListener
calls:
- [setContainer, ['#service_container']]
tags:
- { name: doctrine.event_subscriber, connection: default, priority: -256 }
I have this event listener below, but it is not working:
<?php
namespace Project\BackendBundle\EventListener;
//src/Project/BackendBundle/EventListener/ClippedImagesManager.php
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Project\BackendBundle\Entity\Subitem;
class ClippedImagesManager
{
public function preUpdate(LifecycleEventArgs $args)
{
die("Event listener!!!");
}
//src/Project/BackendBundle/Resources/config/services.yml
services:
project.clipped_images_manager:
class: Project\BackendBundle\EventListener\ClippedImagesManager
tags:
- { name: doctrine.event_listener, event: preUpdate }
I expected "Event listener!!" was fired when updating any entity inside BackendBundle.
I had a similar issue before.
Stripped off example below is same as yours but to see the full working example visit the post please. The trick is, persisting after preUpdate() within postFlush() event.
Note: Although this might not be the best solution, it could be done with an Event Subscriber or simple onFlush() -> $uow->getScheduledEntityUpdates() in an Event Listener.
Service.yml
services:
entity.event_listener.user_update:
class: Site\FrontBundle\EventListener\Entity\UserUpdateListener
tags:
- { name: doctrine.event_listener, event: preUpdate }
Event Listener
<?php
namespace Site\FrontBundle\EventListener\Entity;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Site\FrontBundle\Entity\User;
class UserUpdateListener
{
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
// False check is compulsory otherwise duplication occurs
if (($entity instanceof User) === false) {
// Do something
}
}
}
My postUpdate function fire only when the change is by a form submit, when the change comes by an update function as the following it wont fire.
my post update function :
public function postUpdate(LifecycleEventArgs $args) {
error_log('in post update');
}
the life event declaration :
custom.doctrine.listeer:
class: Custom\CoreBundle\Listeners\CustomDoctrineListener
tags:
- { name: doctrine.event_listener, event: postUpdate, method: postUpdate }
- { name: doctrine.event_listener, event: prePersist, method: prePersist }
the call that don't triggers the the life event :
public function setPrivate($id,$private){
$qb=$this->createQueryBuilder('cc')
->update()
->set('cc.private',$private)
->where('cc.id='.$id);
return $qb->getQuery()->getResult();
}
Use the entity manager to trigger the lifecycle event rather than writing the update query yourself.
// get the entity manager as $em ($em = ...)
$cc = $em->getRepository('your:repository')->find($id);
$cc->setPrivate($private);
$em->flush();
Check out Doctrine Lifecycle Events
Event Listener example below works fine for prePersist() and postPersist() however browser times out for preUpdate() and postUpdate(). Anyone knows why this is happening?
Note: Event listener is the one which cause problem because controller works fine when used on its own. I checked the database.
Error:
Maximum execution time of 30 seconds exceeded in /var/www/html/local/listener/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 685
Event listener
class UserListener
{
//public function postUpdate(LifecycleEventArgs $args)
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof User) {
$userLog = new UserLog();
$userLog->setDescription('User Update.');
$em = $args->getEntityManager();
$em->persist($userLog);
$em->flush();
}
}
}
Controller:
public function updateUser()
{
$repo = $this->getDoctrine()->getRepository('SiteFrontBundle:User');
$user = $repo->findOneBy(array('id' => 1));
$user->setLock(true);
$em = $this->getDoctrine()->getManager();
$em->flush();
}
Service.yml
services:
entity.event_listener.user:
class: Site\FrontBundle\EventListener\Entity\UserListener
tags:
- { name: doctrine.event_listener, event: preUpdate }
preUpdate is very limited.
From: http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#preupdate
Any calls to EntityManager#persist() or EntityManager#remove(),
even in combination with the UnitOfWork API are strongly discouraged and
don’t work as expected outside the flush operation.
Probably need to use onFlush, http://doctrine-orm.readthedocs.org/en/latest/reference/events.html#onflush
Might also want to take look at how these guys do it: https://github.com/Atlantic18/DoctrineExtensions/tree/master/lib/Gedmo/Loggable
Or just setup a logging entity manager.
I want add new Feed item on entity persist and update. I write this event listener (postUpdate is same):
public function postPersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$em = $args->getEntityManager();
if ($entity instanceof FeedItemInterface) {
$feed = new FeedEntity();
$feed->setTitle($entity->getFeedTitle());
$feed->setEntity($entity->getFeedEntityId());
$feed->setType($entity->getFeedType());
if($entity->isFeedTranslatable()) {
$feed->getEnTranslation()->setTitle($entity->getFeedTitle('en'));
}
$em->persist($feed);
$em->flush();
}
}
But I got
Integrity constraint violation: 1062 Duplicate entry '30-2' for key
'PRIMARY'
and in log a have two insertations:
INSERT INTO interview_scientificdirection (interview_id,
scientificdirection_id) VALUES (?, ?) ([30,2]) INSERT INTO
interview_scientificdirection (interview_id, scientificdirection_id)
VALUES (?, ?) ([30,2])
scientificdirection is Many to Many relationship table for entity what we want to persist.
In frontend application everything work fine, but in Sonata Admin I got this problem :(
If you need to persist additional objects, the postPersist or postUpdate handler in Doctrine is, sadly, not the right place to go. I struggled with the same problem today, as I needed to generate some message entries in that handler.
The problem at this point is that the postPersist handler is called during the flush event, and not after. So you can't persist additional objects here, as they are not getting flushed afterwards. Additionally, you can't call flush during an postPersist handler, as this might lead to ducplicate entries (as you have experienced).
One way to go is using the onFlush handler from doctrine, documented here: https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/events.html#onflush
This is just problematic if you need the inserted ids of the database object, as the entity hasn't yet been written to the database in that handler. If you don't need those ids, you are fine with the onFlush event in doctrine.
For me, the solution was a little different. I'm currently working on a symfony2 project, and needed the ids of the inserted database objects (for callbacks and updates later on).
I created a new service in symfony2, which basically just acts like a queue for my messages. During the postPersist update, I just fill the entries in the queue. I have another handler registered on kernel.response, which then takes those entries and persists them to the database. (Something along the line of this: http://symfony.com/doc/current/cookbook/service_container/event_listener.html)
I hope I don't digress too much from the topic here, but as it is something I really struggled with, I hope that some people might find this useful.
The service entries for this are:
amq_messages_chain:
class: Acme\StoreBundle\Listener\AmqMessagesChain
amqflush:
class: Acme\StoreBundle\Listener\AmqFlush
arguments: [ #doctrine.orm.entity_manager, #amq_messages_chain, #logger ]
tags:
- { name: kernel.event_listener, event: kernel.response, method: onResponse, priority: 5 }
doctrine.listener:
class: Acme\StoreBundle\Listener\AmqListener
arguments: [ #logger, #amq_messages_chain ]
tags:
- { name: doctrine.event_listener, event: postPersist }
- { name: doctrine.event_listener, event: postUpdate }
- { name: doctrine.event_listener, event: prePersist }
You can't use the doctrine.listener for this, as this leads to a circular dependency (as you need the entity manager for the service, but the entity manager needs the service....)
That worked like a charm. If you need more info on that, don't hesitate to ask, I'm glad to add some examples to this.
The answer from Francesc is wrong, as the changesets in the postFlush event are already empty.
The second answer of jhoffrichter could work, but is overkill.
The right way to go is to persist the entity in the postPersist event and to call flush again in the postFlush event. But you have to do this only if you changed something in the postPersist event, otherwise you create an endless loop.
public function postPersist(LifecycleEventArgs $args) {
$entity = $args->getEntity();
$em = $args->getEntityManager();
if($entity instanceof FeedItemInterface) {
$feed = new FeedEntity();
$feed->setTitle($entity->getFeedTitle());
$feed->setEntity($entity->getFeedEntityId());
$feed->setType($entity->getFeedType());
if($entity->isFeedTranslatable()) {
$feed->getEnTranslation()->setTitle($entity->getFeedTitle('en'));
}
$em->persist($feed);
$this->needsFlush = true;
}
}
public function postFlush(PostFlushEventArgs $eventArgs)
{
if ($this->needsFlush) {
$this->needsFlush = false;
$eventArgs->getEntityManager()->flush();
}
}
The solution from jhoffrichter is working very well. If you use Console Commands you should add a tag for the event command.terminate. Otherwise it is not working inside Console Commands. See https://stackoverflow.com/a/19737608/1526162
config.yml
amq_messages_chain:
class: Acme\StoreBundle\Listener\AmqMessagesChain
amqflush:
class: Acme\StoreBundle\Listener\AmqFlush
arguments: [ #doctrine.orm.entity_manager, #amq_messages_chain, #logger ]
tags:
- { name: kernel.event_listener, event: kernel.response, method: onResponse, priority: 5 }
- { name: kernel.event_listener, event: command.terminate, method: onResponse }
doctrine.listener:
class: Acme\StoreBundle\Listener\AmqListener
arguments: [ #logger, #amq_messages_chain ]
tags:
- { name: doctrine.event_listener, event: postPersist }
- { name: doctrine.event_listener, event: postUpdate }
- { name: doctrine.event_listener, event: prePersist }
Well, heres how i have done in SF 2.0 and 2.2:
Listener class:
<?php
namespace YourNamespace\EventListener;
use Doctrine\ORM\Mapping\PostPersist;
/*
* ORMListener class
*
* #author: Marco Aurélio Simão
* #description: Listener para realizar operações em qualquer objeto manipulado pelo Doctrine 2.2
*/
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\Common\EventArgs;
use Doctrine\ORM\Mapping\PrePersist;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Mapping\PostUpdate;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\PreFlushEventArgs;
use Enova\EntitiesBundle\Entity\Entidades;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Enova\EntitiesBundle\Entity\Tagged;
use Enova\EntitiesBundle\Entity\Tags;
class ORMListener
{
protected $extra_update;
public function __construct($container)
{
$this->container = $container;
$this->extra_update = false;
}
public function onFlush(OnFlushEventArgs $args)
{
$securityContext = $this->container->get('security.context');
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$cmf = $em->getMetadataFactory();
foreach ($uow->getScheduledEntityInsertions() AS $entity)
{
$meta = $cmf->getMetadataFor(get_class($entity));
$this->updateTagged($em, $entity);
}
foreach ($uow->getScheduledEntityUpdates() as $entity)
{
$meta = $cmf->getMetadataFor(get_class($entity));
$this->updateTagged($em, $entity);
}
}
public function updateTagged($em, $entity)
{
$entityTags = $entity->getTags();
$a = array_shift($entityTags);
//in my case, i have already sent the object from the form, but you could just replace this part for new Object() etc
$uow = $em->getUnitOfWork();
$cmf = $em->getMetadataFactory();
$meta = $cmf->getMetadataFor(get_class($a));
$em->persist($a);
$uow->computeChangeSet($meta, $a);
}
}
Config.yml:
services:
updated_by.listener:
class: YourNamespace\EventListener\ORMListener
arguments: [#service_container]
tags:
- { name: doctrine.event_listener, event: onFlush, method: onFlush }
Hope it helps ;)