2012-06-15 133 views
4

我有一個表A參考表B.學說映射未找到案例

編輯:使用的數據庫引擎是MyISAM。

主義映射得好好的,除非我在DB非有效的情況下在表裏的參考ID不表B中真的存在

所以,當你執行這個代碼:

$objectB = $objectA->getObjectB();//with lazy load 

你實際上得到$ objectB代理對象,它是非空。所以!empty($ objectB)會通過。

當您嘗試訪問$對象B的任何財產,如:

$ objectB->的getName();

你會得到未找到實體例外。在代碼中沒有辦法預測$ objectB實際上不存在,並且$ objectB沒有Name屬性。

$ objectB實際上應該設置爲null,但不會發生。

休眠實際上已經映射屬性未找到=忽略它設置丟失的對象到NULL代替它設置爲代理對象的。教義有沒有類似的東西?

PS。當然,你總是可以捕捉到Entity未發現的異常,並且玩弄它。或者,您可以映射表A上的實際objectB_ID字段,但這些不是100%乾淨的解決方案。

我希望有人有答案。

謝謝

+0

如果你希望從一個getter對象並將其返回null,也許你應該測試空第一。這有點複雜,因爲Doctrine爲你創建代理對象,但原理是一樣的。 – hafichuk

+0

這正是我的問題。你如何測試一個在數據庫中沒有物理記錄的代理對象? – Goran

回答

7

除了當我在DB非有效的情況下在表裏的參考ID不表B中

真的存在IMO這是垃圾的情況下,垃圾出來。如果您有TableA在TableB中可能有或沒有行的模式,您應該在TableB上實現FK約束,以便如果從TableB中刪除行,引用已刪除行的TableA中的任何行將其值更改爲null。

如果你真的想推動關於你提出的架構實現,你可以嘗試這樣做:

$rowExists = ($objectA->getObjectB()->getId() > 0) ? true : false; 

當然,這是假設你tableB的有一個id列,它是無符號。

- 更新 -

try { 
    $objectB = $objectA->getObjectB(); 
} catch (Exception $e) { 
    $objectB = null; 
} 

if ($objectB instanceof ClassB) { 
    // Do work 
} 
+1

如果你運行這個:$ objectA-> getObjectB() - > getId()> 0?真假;你會得到_Entity not found_異常拋出。另一件事是 – Goran

+0

。使用的數據庫引擎是MyISAM,所以FK約束在這種情況下是無法解決的。是的,數據庫中存在大量垃圾。但我正在爲這種情況尋找教義解決方案。 – Goran

+0

你試過我的建議嗎?它應該工作。 –

1

如果你看看你生成的代理類的一個,你會看到__load()__clone()功能都扔EntityNotFoundException

__load()函數在您「懶洋洋地」調用getName()函數時調用。

class ObjectB extends \Foo\Entity\ObjectB implements \Doctrine\ORM\Proxy\Proxy 
{ 
    private $_entityPersister; 
    private $_identifier; 
    public $__isInitialized__ = false; 
    public function __construct($entityPersister, $identifier) 
    { 
     $this->_entityPersister = $entityPersister; 
     $this->_identifier = $identifier; 
    } 
    /** @private */ 
    public function __load() 
    { 
     if (!$this->__isInitialized__ && $this->_entityPersister) { 
      $this->__isInitialized__ = true; 

      if (method_exists($this, "__wakeup")) { 
       // call this after __isInitialized__to avoid infinite recursion 
       // but before loading to emulate what ClassMetadata::newInstance() 
       // provides. 
       $this->__wakeup(); 
      } 

      if ($this->_entityPersister->load($this->_identifier, $this) === null) { 
       throw new \Doctrine\ORM\EntityNotFoundException(); 
      } 
      unset($this->_entityPersister, $this->_identifier); 
     } 
    } 
... 
    public function getName() 
    { 
     $this->__load(); 
     return parent::getName(); 
    } 
... 
} 

你基本上有兩個選擇,第一個是使用try/catch塊。

try { 
    $name = $objectB->getName(); 
} catch (\Doctrine\ORM\EntityNotFoundException $e) { 
    $name = null; 
} 

或者,你可以看看在ObjectB自己實現__wakeup()功能,並可能處理這個(雖然你將很可能需要到反正拋出一個異常)。

最後,如果您感覺雄心勃勃,可以更改Proxy模板。 \Doctrine\ORM\Proxy\ProxyFactory包含模板。

/** Proxy class code template */ 
    private static $_proxyClassTemplate = 
'<?php 

namespace <namespace>; 

/** 
* THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. 
*/ 
class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy 
{ 
    private $_entityPersister; 
    private $_identifier; 
    public $__isInitialized__ = false; 
    public function __construct($entityPersister, $identifier) 
    { 
     $this->_entityPersister = $entityPersister; 
     $this->_identifier = $identifier; 
    } 
    /** @private */ 
    public function __load() 
    { 
     if (!$this->__isInitialized__ && $this->_entityPersister) { 
      $this->__isInitialized__ = true; 

      if (method_exists($this, "__wakeup")) { 
       // call this after __isInitialized__to avoid infinite recursion 
       // but before loading to emulate what ClassMetadata::newInstance() 
      // provides. 
       $this->__wakeup(); 
      } 

      if ($this->_entityPersister->load($this->_identifier, $this) === null) { 
       throw new \Doctrine\ORM\EntityNotFoundException(); 
      } 
      unset($this->_entityPersister, $this->_identifier); 
     } 
    } 

你應該能夠只是在__load()__clone()功能去除EntityNotFoundException的投擲,雖然可能會有意想不到的副作用脫身。如果您計劃定期升級Doctrine,您也可能希望將此更改視爲修補程序。

+0

我們有來自傳統應用的舊數據,這是非常不一致的。手動清理數據非常困難。如果我在禁用外鍵檢查的情況下在表中加載數據,則會收到EntityNotFoundException。有沒有辦法處理這個異常,並設置外鍵列的值爲NULL,並重新拋出同樣的異常,如果教條不能找到相關的實體,以便數據將自動cleanedup? – vishal

0

用它在你的註釋中的對象A類:

@ORM\ManyToOne(targetEntity="ObjectB", fetch="EAGER") 
0

我們不得不使用Doctrine ORM這個問題,即對象A是足夠重要的是,我們不想簡單地使用某種清理腳本,如果將其刪除對象A不再存在。這在正常應用程序執行期間不會發生,但在手動編輯數據庫表時非常少見,例如在遷移/升級期間。

幾個選項,我們考慮,但不是太熱衷於:

  • 使用供應商特定的修復,在這種情況下,ON DELETE SET NULL約束爲SQL Server)。
  • 使用自定義的Twig擴展來捕獲EntityNotFoundException(我們在Twig模板中只是真的遇到了這個問題,但不想在任何地方添加擴展,然後仍然可能在我們的PHP控制器中有問題)。

相反,我們決定通過捕獲EntityNotFoundException來污染我們的實體,但要包含EntityExistanceCheckableTrait特性的所有邏輯。

一旦性狀被添加到對象A和對象B中,那麼我們所需要做的就是在Twig中調用$objectB->hasObjectB(){{ objectB.hasObjectA() }}並根據控制器/模板的邏輯進行處理。

class ObjectA 
{ 
    use EntityExistanceCheckableTrait; 

    ... 
} 

class ObjectB 
{ 
    use EntityExistanceCheckableTrait; 

    ... 

    public function hasObjectB() 
    { 
     return $this->hasEntity('ObjectB'); 
    } 
} 

的特點增加了__invoke()PHP魔術方法簡單地返回true,如果它存在,但是這是我們需要做代理的負載,看看實際的關聯實體eixsts或者僅僅是關聯實體中的孤兒ID。

這裏是特質的全碼:

/** 
* Add to the entities on both sides of a Doctrine Association then implement a wrapper around hasEntity() in the 
* owning entity. 
* 
* Trait EntityExistanceCheckableTrait 
*/ 
trait EntityExistanceCheckableTrait 
{ 
    /** 
    * This can be empty but needs to be defined so we can check that the entity is callable. 
    * 
    * @return bool 
    */ 
    public function __invoke() 
    { 
     return true; 
    } 

    /** 
    * @param string $entityName 
    * @return bool 
    */ 
    public function hasEntity($entityName) 
    { 
     // Create the callable method 
     $entityMethod = 'get'.$entityName; 

     try { 
      $entity = $this->$entityMethod(); 

      // We invoke the associated entity to force the real entity to be lazy-loaded instead of the proxy. 
      // This way we can ensure that we don't have an orphan entity (e.g. a proxy with the ID of the associated 
      // entity, loaded from the original entity, but the associated entity no longer exists in the database). 
      return (isset($entity) && $entity()) ? true : false; 
     } catch (EntityNotFoundException $e) { 
      return false; 
     } 
    } 
}