2017-06-19 55 views
1

在Symfony 3項目中,我有一個實體,我想審覈某些屬性中的更改,所以我可以創建一個事件偵聽器來存儲它們。Symfony&Doctrine:使用Event Listener在表中記錄變化

更多或更少的實體如下:

  • ReceivedEmail:代理和caseDetail是我希望審計
  • ReceivedEmailChange屬性:previousAgent,currentAgent和previousCaseDetail和currentCaseDetail

而且EventListener看起來如下

/** 
    * @param OnFlushEventArgs $args 
    */ 
    public function onFlush(OnFlushEventArgs $args) 
    { 
     /** @var ReceivedEmail $entity */ 
     $entityManager = $args->getEntityManager(); 
     $unitOfWork = $entityManager->getUnitOfWork(); 

     $updates = $unitOfWork->getScheduledEntityUpdates(); 

     foreach ($updates as $entity) { 

      if ($entity instanceof ReceivedEmail) { 

       $changes = $unitOfWork->getEntityChangeSet($entity); 

       $this->receivedEmailChanges[] = $this->receivedEmailChangeManager 
        ->getReceivedEmailChanges($entity, $changes); 
      } 
     } 
    } 

public function postFlush(PostFlushEventArgs $args) 
    { 
     $em = $args->getEntityManager(); 

     $i = 0; 
     foreach($this->receivedEmailChanges as $receivedEmailChange) { 
      $em->persist($receivedEmailChange); 
      unset($this->receivedEmailChanges[$i]); 
      $i++; 
     } 

     if ($i > 0) { 
      $em->flush(); 
     } 
    } 

問題是在postFlush方法調用$ entityManager->的flush()在infinte循環結束,並在此錯誤:

PHP Fatal error: Maximum function nesting level of '256' reached, aborting! in /var/www/sellbytel/vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php on line 187

所以我的問題是:我怎麼能更改保存在數據庫中從EventListener是否可能?如果沒有,是否有解決方法來創建此日誌?

謝謝!

+0

恕我直言,你不能使用'onFlush'事件來定義你需要處理的變化數組,而不是在'postFlush'中處理這些變化之後再次開始整個過程​​。您可以嘗試使用'preUpdate'和'postUpdate'事件手動保存舊值和新值,而不是依靠unitOfWork來替換onFlush偵聽器。 – lordrhodos

+0

[Doctrine2的可記錄行爲](https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/loggable.md)可能對您有用。 – ccKep

回答

0

如果User的用戶名或密碼字段發生更改,則將日誌寫入UserAudit表中,以便與數據庫觸發器具有相同的邏輯。您可以根據自己的需求進行調整。

編號:http://www.inanzzz.com/index.php/post/ew0r/logging-field-changes-with-a-trigger-like-event-listener-for-auditing-purposes

services.yml

services: 
    application_backend.event_listener.user_entity_audit: 
     class: Application\BackendBundle\EventListener\UserEntityAuditListener 
     arguments: [ @security.context ] 
     tags: 
      - { name: doctrine.event_listener, event: preUpdate } 
      - { name: doctrine.event_listener, event: postFlush } 

監聽

namespace Application\BackendBundle\EventListener; 

use Application\BackendBundle\Entity\User; 
use Application\BackendBundle\Entity\UserAudit; 
use Doctrine\ORM\Event\PostFlushEventArgs; 
use Doctrine\ORM\Event\PreUpdateEventArgs; 
use Symfony\Component\Security\Core\SecurityContextInterface; 

class UserEntityAuditListener 
{ 
    private $securityContext; 
    private $fields = ['username', 'password']; 
    private $audit = []; 

    public function __construct(SecurityContextInterface $securityContextInterface) 
    { 
     $this->securityContext = $securityContextInterface; 
    } 

    public function preUpdate(PreUpdateEventArgs $args) // OR LifecycleEventArgs 
    { 
     $entity = $args->getEntity(); 

     if ($entity instanceof User) { 
      foreach ($this->fields as $field) { 
       if ($args->getOldValue($field) != $args->getNewValue($field)) { 
        $audit = new UserAudit(); 
        $audit->setField($field); 
        $audit->setOld($args->getOldValue($field)); 
        $audit->setNew($args->getNewValue($field)); 
        $audit->setUser($this->securityContext->getToken()->getUsername()); 

        $this->audit[] = $audit; 
       } 
      } 
     } 
    } 

    public function postFlush(PostFlushEventArgs $args) 
    { 
     if (! empty($this->audit)) { 
      $em = $args->getEntityManager(); 

      foreach ($this->audit as $audit) { 
       $em->persist($audit); 
      } 

      $this->audit = []; 
      $em->flush(); 
     } 
    } 
} 
0

那豈不是最簡單的,只需使用一個已經存在的捆綁,如simplethings/entity-audit-bundle?您可以分別提供要審覈哪些實體。

或者您是否打算手動執行此操作?在這種情況下,請忽略我的答案。

+0

這不是一個答案,這是一個評論 –

+0

whelp猜測我太累了,甚至沒有注意到 – Kapparina