2016-02-12 18 views
6

比方說,我有一個原則的(第2版)實體如下:教義,如何實現額外的模型功能的實體

<?php 
namespace AppBundle\Entity; 

use Doctrine\ORM\Mapping as ORM; 

/** 
* User 
* 
* @ORM\Table(name="users") 
* @ORM\Entity 
*/ 
class User { 
    /** 
    * @var integer 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="IDENTITY") 
    */ 
    private $id 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="name", type="string", length=50, nullable=false) 
    */ 
    private $name; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="group_id", type="string", length=6, nullable=true) 
    */ 
    private $groupId; 

    // Getters and setters... 
} 

現在,我想管理User的關係,以Group一些條件,如:

  1. 返回NULL(或某種\AppBundle\Entity\Group具有固定值的骨架/模板不是從數據庫加載)如果不存在,即使它被設置爲一個值(在數據庫設置爲阻止這種行爲沒有鍵限制),所以某種驗證/檢查的要求
  2. 延遲加載Group,呼籲$user->getGroup()

我正在閱讀谷歌,我很困惑如何實現正確(符合Doctrine/Symfony的方式)。

我可以一個多對一添加到實體類的關係是這樣的:

/** 
* @var \AppBundle\Entity\Group 
* 
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Group") 
* @ORM\JoinColumns({ 
* @ORM\JoinColumn(name="group_id", referencedColumnName="id") 
* }) 
*/ 
private $group; 

但後來如何防止因不存在的,外鍵異常?即我想檢索用戶的組詳細信息,當它在我的數據庫中可用時,但當它不是我不想Doctrine拋出一個異常崩潰我的應用程序。

人們說,使用實體管理器Entityvery bad practice,我同意。但我爲此迷惑了Proxy Objects或​​。

似乎可以這樣使用Models,但我找不到任何有關如何在Doctrine2中正確實施它們的強大文檔。

如果可以,請幫助。在Kohana它是如此簡單(但這種方式不成熟)。


編輯:

@Massimiliano Fedel建議嘗試在User::getGroup()方法捕獲異常,最終使不存在的組返回作爲NULL

所以我COMMITED驗證碼:

/** 
* Get group 
* 
* @return \AppBundle\Entity\Group 
*/ 
public function getGroup() { 
    try { 
     $group = $this->group; 
    } catch (\Doctrine\ORM\EntityNotFoundException $e) { 
     $group = null; 
    } 
    return $group; 
} 

可惜的是,似乎例外,不能釣到這樣,由於框架與Doctrine\ORM\EntityNotFoundException退出:

Entity of type 'AppBundle\Entity\Group' for IDs id(999) was not found 

EDIT 2 :

下面你可以找到d一些User流程的基礎架構,即爲什麼我不能確保在我的數據庫中所有Users都有可用的Groupshttp://oi64.tinypic.com/1sfno5.jpg

+0

你能解釋一下更多的實際用例嗎?假設我們有一個不在數據庫中的用戶。您希望在請求之間發生什麼? – Cerad

+0

@Cerad你是什麼意思「請求之間」? – roomcays

+0

所以你讓一個用戶擁有一個不在數據庫中的組,顯然你不想真的堅持這個組。 (爲什麼我不知道)。所以現在用戶導航到其他頁面。你是否期望$ user對象仍然具有相同的組?如果是這樣,該組將存儲在哪裏?如果我們知道你爲什麼想要這種功能,那麼也許我們可以提供更多的建議。 – Cerad

回答

1

1)你有沒有嘗試捕捉「組」的getter方法內的異常?這樣您就可以捕獲異常並在發生異常時返回「null」。

2)從教條2.1的文檔:「協會默認標記爲懶惰,這意味着一個協會的整個集合對象被第一次訪問時填充。」 http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html

+0

1.看起來不錯,嘗試),2.這將做,謝謝 – roomcays

+0

我已經嘗試了你所建議@Massimiliano,但它沒有奏效 - 請參閱我的第一個**編輯**在問題的身體。 – roomcays

1

那麼,如何創建一個服務,這將包裝在User實體上的CRUD操作,比如說UserService?這樣,您可以將group_id保持原樣並添加group字段,該字段不受管理(沒有註釋,不會保留到數據庫)。

UserService將有getGroup方法,這將需要User作爲參數,然後檢索他的group_id和使用EntityManager(或實際上GroupService)來獲取Group實體,如果沒有被發現,你將返回null,否則就會將返回的Group設置爲實體的非託管字段,因此下次無需再次獲取它。

+0

這個想法看起來很有希望,儘管我仍然更願意使用一些「模型相似」的包裝(但「服務」可能只是另一個名稱)。我現在正在嘗試這個想法,1月份。我還不知道如何輕鬆地將'User'(作爲實體)方法和屬性代理到'UserService'。也許你也有這個想法嗎? – roomcays

+0

我不會代理他們。實體應該僅僅是數據的持有者(用很好的方式包裝它,用方法,當試圖設置令人討厭的東西時可以抗議,但就是這樣)。這就是爲什麼教義2從Active Record模式轉移到Data Mapper的原因,我認爲這是一個很大的改進。這也是爲什麼在實體中使用EntityManager被認爲是不好的做法 - 實體被認爲是愚蠢的對象。另一方面,服務可以很聰明並且「一手」,因此您需要服務容器。看看這個:http://symfony.com/doc/current/book/service_container.html –

0

好的,首先,您的User - Group關係沒有正確定義。您絕對需要添加多對一關係:

/** 
* @var \AppBundle\Entity\Group 
* 
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Group") 
* @ORM\JoinColumns({ 
* @ORM\JoinColumn(name="group_id", referencedColumnName="id") 
* }) 
*/ 
private $group; 

一些有關此字。在JoinColumns子句中,默認情況下,該關係是可以爲空的,這意味着您可以在產生的group_id列(Doctrine將爲您創建的連接列)中使用NULL而不是外鍵。

如果你想有一個非空的關係,定義已去喜歡這個:

/** 
* @var \AppBundle\Entity\Group 
* 
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Group") 
* @ORM\JoinColumns({ 
* @ORM\JoinColumn(name="group_id", referencedColumnName="id", nullable=false) 
* }) 
*/ 
private $group; 

所以,在默認情況下,你可以有一個NULL組的User。一旦這個正確定義,關於您的問題:

1.

如果您的應用程序正確地創建,你永遠不應該涉及到還未被提交到數據庫的Group一個User結束。每個Group你與一個User綁在一起,在堅持User本身之前必須以某種方式從教義中獲得。無論是從應用程序還是從表單。如果您需要在持續User之前驗證Group存在,則必須通過100%確定您使用的任何Group來自數據庫來解決上游問題。

在這個特定背景下,setGroup二傳手應該已經確保所提供的是確實是一個Group實體(如果不是你應該添加一個typehint)和學說已經確保了此實體已經存在於數據庫中。我同意學說錯誤可能會變得醜陋,但交付的產品不應該包含任何這些錯誤。

瞭解Symfony驗證通常與表單一起使用,目的是驗證最終用戶引入的潛在違規行爲,而不是開發人員(通常)。

2.

有了正確定義的關係,如上圖所示,學說將自動照顧這對你。默認情況下,所有Doctrine關係都是懶惰加載的。

另外:

/** 
* Get group 
* 
* @return \AppBundle\Entity\Group 
*/ 
public function getGroup() { 
    try { 
     $group = $this->group; 
    } catch (\Doctrine\ORM\EntityNotFoundException $e) { 
     $group = null; 
    } 
    return $group; 
} 

你並不需要這樣做。去掉它。

+0

Zephyr,謝謝你的回答。但是,這並不適用於我的情況,因爲我嘗試基於前一段已定義的數據庫架構來構建Symfony + Doctrine應用程序。我不能(也永遠不能)確保'User'和'Group'之間有效的現有關係(請參見[我的評論](http://stackoverflow.com/questions/35363940/in-doctrine-how-to-achieve) - 附加模型 - functionallities換實體#評論-58436756))。 **'用戶'可以擁有與不存在的組**有關的'group_id',我不能改變這個。這就是爲什麼我需要一些另外的,可能更抽象的方法。 – roomcays

+0

好吧,那麼您的應用程序是負責添加用戶組內容,還是隻能讀取它?這是一種重構還是使用外部數據?您在上面說過,您實際上正在嘗試設置API-API通信,但Doctrine可能與通過(比如說)Rest API進行通信完全不同。 – Zephyr

+0

我的應用程序負責添加'User'內容,並且'Groups'是(比如說)只讀的:我可以「綁定」_some_'Users'的固定數量的組。現在,更深入的描述:我的應用程序連接到外部API,它從那裏加載'Users',然後將它們存儲在本地數據庫中。我的應用程序的其他模塊生成所有本地存儲的「用戶」報告,並通過HTML頁面上的表格呈現給我們。可以與本地組連接的這些用戶會顯示一些額外的組詳細信息。對於其他人,團體細節被替換爲一些虛擬數據。 – roomcays

0

考慮到您的具體使用情況,您最好的選擇是Doctrine event subscribers

請注意,因爲它們與常規Symfony event subscribers分開。後者與通用的Symfony工作流程有關,前者專用於Doctrine。

具體來說,postLoad event是你所需要的。這裏的想法是:

  1. 一個group屬性添加到User相應typehinted getter和setter
  2. 創建自定義學說的用戶,被稱爲MyBundle\Entity\EventSubscriber\UserSubscriber
  3. 與主義實體管理器服務
  4. 它注入創建這樣的postLoad方法:

    public function postLoad(LifecycleEventArgs $args) 
    { 
        $user = $args->getEntity(); 
    
        if ($user instanceof User && $user->getGroupId()) { 
         $r = $this->em->getRepository('MyBundle:Group'); 
         $group = $r->find($user->getGroupId()); 
    
         if ($group instanceof Group) { 
          $user->setGroup($group); 
         } 
    
         // If not an instance of Group, no group can be found: do nothing and leave it NULL 
        } 
    } 
    

無論何時從管理器加載Doctrine實體,都會調用postLoad方法,這意味着通過Doctrine找到的所有實體都將盡可能使用它們的組填充。

如果你想延遲加載...首先,你確定你想要這個嗎? User組通常用在Symfony應用程序的大多數頁面中 - 例如在授權檢查中。如果組在每一頁都被加載,那真的不值得。

如果你絕對需要這個,你可以使用學說代理工廠服務並填充User與代理,而不是實際的實體(不知道這是如何工作完全一致):

$group = $this->em->getProxyFactory()->getProxy('MyBundle:Group', $user->getGroupId()); 
$user->setGroup($group); 

在這種情況下,你必須注意以下,因爲有些裝載嘗試將失敗如果ID不存在:

/** 
* Get group 
* 
* @return \AppBundle\Entity\Group 
*/ 
public function getGroup() 
{ 
    static $loaded = false; 

    if (!$loaded) { 
     try { 
      $this->group; 
     } catch (\Doctrine\ORM\EntityNotFoundException $e) { 
      $this->group = null; 
     } 
     $loaded = true; 
    } 

    return $this->group; 
} 

我調整揚母馬的原始代碼以getGroup()每次調用主義會試圖加載Group

+0

謝謝Zephyr的參與。在賞金時間結束後的12個小時內,我將無法檢查您的解決方案,但看起來令人信服。我不知道* Doctrine事件訂閱者*,這似乎是一個非常強大的工具來幫助處理特殊情況。這是我正在尋找的那種幫助。我還要感謝Jan Mares和Massimiliano Fedel。我會盡快將該答案標記爲正確答案(1-3天)。 – roomcays