2012-05-29 48 views
22

我需要檢查持久化實體是否已更改,並且需要在數據庫上進行更新。 我做了什麼(沒有工作)是以下幾點:如何檢查實體是否在Doctrine 2中更改?

$product = $entityManager->getRepository('Product')->find(3); 
$product->setName('A different name'); 

var_export($entityManager->getUnitOfWork()->isScheduledForUpdate($product)); 

該代碼打印總是false,我也試圖刷新前檢查工作的單位,但沒有奏效。

任何人有一個建議?

+0

當您發現實體發生了變化時,您想完成什麼? –

+0

如果您查看'UnitOfWork'的源代碼,在'isScheduledForUpdate'的註釋中,他們會說「注意:目前不太有用,因爲髒實體只在提交時註冊」 –

+0

呃...我需要發送一個當產品發生變化時向系統用戶發出通知,但是當產品數據沒有改變時我應該忽略通知(我將表單的POST數據綁定到實體以使用該Doctrine易用性後,我仍然會進行刷新)。我會嘗試之前執行UnitOfWork :: computeChangeSets(),可能會降低性能,但可以工作。 – eagleoneraptor

回答

21

我會檢查你的的setName功能實際上是做一些事情($這個 - >名稱= $名字......)的第一件事情,如果它已經工作,那麼你可以在你的服務定義事件監聽器.yml在調用flush時觸發。

entity.listener: 
    class: YourName\YourBundle\EventListener\EntityListener 
    calls: 
    - [setContainer, ["@service_container"]] 
    tags: 
    - { name: doctrine.event_listener, event: onFlush } 

然後你定義EntityListener

namespace YourName\YourBundle\EventListener; 

use Doctrine\ORM\Event; 
use Symfony\Component\DependencyInjection\ContainerAware; 

class EntityListener extends ContainerAware 
{ 

    /** 
    * Gets all the entities to flush 
    * 
    * @param Event\OnFlushEventArgs $eventArgs Event args 
    */ 
    public function onFlush(Event\OnFlushEventArgs $eventArgs) 
    { 
     $em = $eventArgs->getEntityManager(); 
     $uow = $em->getUnitOfWork(); 

     //Insertions 
     foreach ($uow->getScheduledEntityInsertions() as $entity) { 
      # your code here for the inserted entities 
     } 

     //Updates 
     foreach ($uow->getScheduledEntityUpdates() as $entity) { 
      # your code here for the updated entities 
     } 

     //Deletions 
     foreach ($uow->getScheduledEntityDeletions() as $entity) { 
      # your code here for the deleted entities 
     } 
    } 
} 

如果您需要知道哪些實體被改變,但他們一直保存到數據庫後,做一些與他們,只存儲實體在私有數組中更改,然後定義一個從數組中獲取實體的onFlush事件。

順便說一句,要觸發這種事件,您需要在實體上添加@ORM \ HasLifecycleCallbacks。

+0

請記住,實體將被取消/插入/刪除,並且使用onFlush事件您不能確定刷新會成功。 – Jekis

+0

還有'$ uow-> isEntityScheduled()'涵蓋了所有的操作 –

+0

嗨,這正是我在找的東西,但是有一個差異:我需要知道表/數據已經改變了,並將它插入到另一個表中,就像將每個更改記錄到數據庫中一樣,可以幫助我解決這個問題嗎? https://stackoverflow.com/questions/44974520/symfony3-save-every-update-into-database –

10

如果您需要訪問具有舊值和新值的實體字段,您也可以查看PreUpdate事件。

<?php 
class NeverAliceOnlyBobListener 
{ 
    public function preUpdate(PreUpdateEventArgs $eventArgs) 
    { 
     if ($eventArgs->getEntity() instanceof User) { 
      if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') { 
       $oldValue = $eventArgs->getOldValue('name'); 
       $eventArgs->setNewValue('name', 'Bob'); 
      } 
     } 
    } 
} 
+0

這就是我一直在尋找的。歡呼 – Sharpy35

8

我並不需要/想爲我的情況下,監聽器,所以我結束了

$product->setName('A different name'); 
$uow = $entityManager->getUnitOfWork(); 
$uow->computeChangeSets(); 
if ($uow->isEntityScheduled($product)) { 
    // My entity has changed 
} 
+0

如果實體上沒有任何變化,則返回true。 – Jessica

+0

@Jessica,或許你的實體在其他地方發生了變化,例如在生命週期回調中。在任何情況下,這個答案中的對象$ uow應該告訴屬性'changesets'下的那個實體已經改變了什麼 – Nicolas

+0

擡頭,如果相關的實體有變化,isEntityScheduled($ entity)'返回'true'。例如。你有'產品#品牌',其中'#品牌'是'品牌'實體。如果您修改了ProductFieldset的子字段集中的品牌名稱,並且沒有更改Product字段的基本字段集,則產品和品牌實體都將具有更新的modifiedAt時間戳。 – Nukeface

2

Doctrine2 Docs. 17. Change Tracking Policies

一個例子的位大多是從提供的鏈接取

如果你像我那樣使用第三種形式(17.3。通知),你可以測試你的實體是否改變了:

$uow = $entityManager->getUnitOfWork(); 
$aChangeSet = $uow->getEntityChangeSet($oEntity); 

如果沒有改變,它會返回空白數組。

2

這個問題相當古老,但可能還有一些人會從不同的角度看待這個問題。 UnitOfWork很好用,但它只返回變化數組。如果有人不知道哪些字段可能發生了變化,只是希望將整個實體作爲對象來比較$oldEntity$newEntity,這可能是一個痛苦。即使該事件的名稱是更新前如果有人將嘗試如下,從數據庫中讀取數據:

$er->find($id); 

返回的實體將包含所有的變化。 解決方法是很簡單,但它也有一些掛鉤:

public function preUpdate(Entity $entity, PreUpdateEventArgs $args) 
{ 
    $entity = clone $entity; //as Doctrine under the hood 
          //uses reference to manage entities you might want 
          //to work on the entity copy. Otherwise,   
          //the below refresh($entity) will affect both 
          //old and new entity. 
    $em = $args->getEntityManager(); 
    $currentEntity = $em->getRepository('AppBundle:Entity')->find($entity->getId()); 
    $em->refresh($currentEntity); 

} 

對於那些誰正在使用其他事件,如前置液,我趕緊檢查了它和解決方法沒有工作,因爲可能是refresh()方法丟棄任何刷新變化,所以需要做的是在監聽器中再次調用flush,並創建一些靜態的$alreadyFlushed切換以避免循環引用。

0

我很好奇Doctrine和每個人記錄postFlush,因爲在某些情況下,你有一個正在進行的交易。 我想指出的還有postTransactionCommit,根據您在postFlush事件中試圖實現的內容,這可能更安全。

0

基於我的需要,這裏的答案和the docs,我想出了一個實體中的modifiedAt時間戳的以下解決方案。

/** 
* @Doctrine\ORM\Mapping\PreUpdate() 
* 
* @param \Doctrine\ORM\Event\PreUpdateEventArgs $args 
* @return $this 
*/ 
public function preUpdateModifiedAt(\Doctrine\ORM\Event\PreUpdateEventArgs $args) 
{ 
    $this->setModifiedAt(new \DateTime('now')); 

    return $this; 
} 

這是基於what the docs say這個Event相對於其他可用的,如PostPersistPreFlush

更新前的是最嚴格的使用事件,因爲它被稱爲 權在爲EntityManager#flush()方法內的實體調用更新語句之前。 請注意,當計算的變更集爲空時,不會觸發此事件 。

使用PreUpdate,而不是其他人可以讓您將所有的計算和計算密集的功能已經被定義學說的過程。手動觸發變更集的計算,例如上面的theseanswers是服務器CPU密集型。像在the accepted answer中使用的onFlush事件是一個選項(以演示的方式),但是如果您依賴於檢測實體的更改,則不會如上面的函數(preUpdateModifiedAt(PreUpdateEventArgs $args))那樣。

相關問題