8

I'm struggling with the following, in a entity class I have a preUpdate lifeCycleCallback which has to persist a new entity before it flushes the changes for a auditTrail.

In preRemove and prePersist this works perfectly but in preUpdate nothing happends. If I call flush myself it goes in a recursive loop.

According to the Google groups for doctrine-user putting it in onFlush should be a option but in that event I can't access the old values of the entity to save this old values in a new other entity for the audittrail.

Some small example what i'm trying to archive:

<?php
/**
 * @Entity
 * @HasLifeCycleCallbacks
 */
class someEntity {
    ... annotations ...


    /**
     * @PreUpdate
     */
    public function addAuditTrail() {
        $em = \Zend_Registry::get('doctrine')->getEntityManager();

        $entity = new AuditTrail();
        $entity->action = 'update';
        $entity->someField = $this->someField;

        $em->persist($entity); //this just doesn't do anything :-(
    }
}
?>

It's not real code, just something to illustrate you what I want. I also tried something like this:

$em->getUnitOfWork()->computeChangeSet($em->getClassMetaData(get_class($entity)), $entity);

Which should work according to this topic: http://groups.google.com/group/doctrine-user/browse_thread/thread/bd9195f04857dcd4

If I call the flush again but that causes Apache to crash because of some infinite loop.

Anyone who got ideas for me? Thanks!

Kees Schepers
  • 2,248
  • 1
  • 20
  • 31

3 Answers3

6

You should never use the entitymanager inside your entities. If you would like to add audit trails, you should map the "SomeEntity" entity to the "AuditTrail" entity and do something like

/**
 * @PreUpdate
 */
public function addAuditTrail() {
    $entity = new AuditTrail();
    $entity->action = 'update';
    $entity->someField = $this->someField;

    $this->autitTrail->add($entity);
}

If you set the cascade option on the mapping, it will get persisted when you persist "SomeEntity".

tvlooy
  • 1,036
  • 10
  • 16
  • Why shouldn't you use the entity manager inside my entities? And if you map the AuditEntity to the SomeEntity it will be associated in the database and that's not what I want to copy my entities and add some other fields like "action" = update etc. This AuditEntity was just a example because for auditing I use: https://github.com/keesschepers/EntityAudit – Kees Schepers Nov 13 '11 at 10:06
  • I would use the eventmanager. Wanted to take a look at your code but no time yet. – tvlooy Nov 16 '11 at 21:03
  • 1
    I finally (a year ago!) used the event manager (EventSubscriber) to do these tasks globally. I have included this in my framework: https://github.com/php-pike/Pike (look at the EntityAudit part) thanks! – Kees Schepers Jun 18 '12 at 07:37
  • See also http://stackoverflow.com/questions/16904462/adding-additional-persist-calls-to-preupdate-call-in-symfony-2-1 – Matt Browne Sep 04 '14 at 20:31
3

I had the same problem in the preUpdate method of an EventListener. I solved this by storing the new entity in a property and moving the new persist() and flush() calls to the postUpdate method.

class someEntity {
... annotations ...

protected $store;

/**
 * @PreUpdate
 */
public function addAuditTrail() {
    //$em = \Zend_Registry::get('doctrine')->getEntityManager();

    $entity = new AuditTrail();
    $entity->action = 'update';
    $entity->someField = $this->someField;

    // replaces $em->persist($entity); 
    $this->store = $entity;
}

/**
 * @PostUpdate
 */
public function saveAuditTrail() {
    $em = \Zend_Registry::get('doctrine')->getEntityManager();
    $em->persist($this->store); 
    $em->flush();
}

}

ACNB
  • 816
  • 9
  • 18
  • I know it seems slightly 'hack like' but I cannot see an 'official' solution to this use case, I have too used this method – Andrew Atkinson Apr 18 '13 at 13:32
  • 1
    Unfortunately, calling `flush()` inside a lifecycle callback is not supported by Doctrine and this doesn't seem to work in newer Doctrine versions; I get this error: http://www.doctrine-project.org/jira/browse/DDC-3218. – Matt Browne Sep 04 '14 at 20:29
  • See this link for how to do it properly: http://stackoverflow.com/questions/16904462/adding-additional-persist-calls-to-preupdate-call-in-symfony-2-1. I wish there were a way to do it inside the entity class though, rather than having to create a separate global class... – Matt Browne Sep 04 '14 at 20:30
0

entitymanager->persist() will not work inside the preUpdate method. Instead of that, you can save the AuditTrail data to session and after the flush of the 'SomeEntity', take the data from session and perform entitymanager->persist(...) and entitymanager->flush()

Darshita
  • 733
  • 1
  • 6
  • 8