2012-11-06 37 views
1

我有一個網站記錄,可以包含任何數量的具有不同信息的AdProvider配置字段。不幸的是,fieldNames(提供商的名稱)是獨一無二的,而且還會有更多。我可以在文檔中將它們中的每一個硬編碼爲散列類型,但每次添加新的提供程序時都必須更新文檔。在Symfony2中與Doctrine ODM如何在文檔中動態創建字段?

我想動態修改尋找供應商,我可以從另一個蒙戈收集得到的列表中選擇文檔本身,但我無法弄清楚如何做到這一點。

我第一次嘗試是建立在loadClassMetaData事件偵聽器和映射新的領域。我看到了字段映射,但它們沒有反映在文檔中。顯然,這些字段沒有任何getter和setter,所以我嘗試使用magic __get和__set方法訪問它們,但是我得到的錯誤不存在。

也許我會對此錯誤的方法嗎?

例蒙戈記錄:

{ 
    "_id" : ObjectId("4ff1d29d99c6667722000000"), 
    "_type" : [ 
     "Models_Site" 
    ], 
    "enabledAdProviders" : [ 
     "provider1", 
     "provider2", 
     "provider3", 
     "provider4" 
    ], 
    "provider1" : { 
     "id" : "4028cbff38e2d7c00666fd2fdc770208" 
    }, 
    "provider2" : { 
     "placements" : { 
      "Top_300x50" : "477", 
      "Btm_300x50" : "478", 
      "Top_320x50" : "477", 
      "Btm_320x50" : "478" 
     } 
    }, 
    "provider3" : { 
     "id" : "8a809449013331fdcdc6662708532b20" 
    }, 
    "siteId" : "PsTl", 
    "siteName" : "Publisher Site", 
    "provider4" : { 
     "placements" : { 
      "Top_300x50" : "430", 
      "Btm_300x50" : "430" 
     } 
    } 
} 

我的監聽器:

<?php 
namespace BIM\DataBundle\Listener; 

use BIM\DataBundle\Document\AdPublisherRecord; 
use BIM\DataBundle\Document\AdProviderRecord; 
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; 

class AdPublisherSiteSetup 
{ 
private $serviceContainer; 

/** 
* This service is called every time Ads doctrine odm loads a document. 
* We are dynamically creating the ad provider setting nodes on the AdPublisher Record 
* 
*/ 
public function __construct($serv){ 
    $this->serviceContainer = $serv; 
} 

public function loadClassMetadata(\Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs $args) 
{ 
    $metaData = $args->getClassMetadata(); 
    $document = (string)$metaData->getName(); 

    if($document == "BIM\DataBundle\Document\AdPublisherRecord"){ 
     //query for ad providers 
     //create as a hash type to store each providers settings. 
     $providerList = $this->serviceContainer->get('ads.publisher.factory')->getProviderList(); 
     foreach ($providerList as $name => $value) { 
      $metaData->mapField(array('fieldName' => $name, 'type' => 'hash')); 
     } 
    } 
} 
} 
+0

我不認爲你想要做的是可能與教條。 Doctrine文檔是「靜態的」,所以你想加載的字段必須存在於文檔中,否則將不會被加載。如果你能解釋更多關於你想達到的目標,我想有一個更好的方法來做到這一點。 – Sgoettschkes

+0

我希望能夠使我的應用程序適用於新輸入的廣告提供商(在創建或編輯網站記錄時生成所需的正確表單字段。 –

+0

我玩過更多的東西,如果我在檢索記錄之前修改classMetadata,就能使它工作。但是,我需要在Document中定義屬性並使用魔術方法,因此看起來這樣做沒有任何優勢。我想我只需要添加屬性,生成getter和setter,並在新的AdProvider添加到數據庫時重新部署。 –

回答

3

你不能用簡單的原則,ODM做到這一點,但是我用了一個簡單的技巧做幾乎一樣:


由於MongodDB文件可以被嵌套,只需在創建@Hash財產您的文檔,這將包含無模式屬性(認爲它作爲一個哈希袋):

class Doc { 

    /** 
    * Schema-less dynamic properties goes here: 
    * @MongoDB\Hash 
    */ 
    public $extraFields; 

    /** @MongoDB\Date */ 
    public $createdDate; 

    //Other static properties... 
} 

唯一需要注意的是,該方案較少的屬性將在$extraFields財產去,而不是在根。

然而,這僅僅是一個美學問題,你可以做這些領域的一切(索引,查詢,地圖降低,ECC)

通過這種方式,你可以有靜態定義的屬性(如身份證,createdDate,ECC ) 和動態屬性的哈希包。

如果你想要記錄的一些這些動態屬性的存在(並避免常見錯字的問題給您的同事),你可以定義文檔中存在的一些訪問方法:

class Doc { 

    public getExtraName() { 
     if(empty($this->extraFields['name'])) return null; 
     return $this->extraFields['name']; 
    } 
} 
1

我寫Symfony2中和Doctrine2-ODM廣泛EAV系統,其中的任何文件可以變成一常規的 「混合」字段和屬性。這些屬性全部在具有特定類型(地址,電話號碼,字符串,文檔參考等)的另一個文檔中定義。我無法分享代碼,因爲它是公司財產,但其要點如下。

  • 屬性的所有值必須能夠簡化爲一個數組(多維也可以),因爲這些值存儲在文檔的散列字段中。
  • 製作一個可以在文檔中擴展的抽象類(AbstractAttributeAware)就是我使用的。該班內部應該有以下內容。
    • 稱爲「updatecount」兩個或東西沿着這些線路的字段。任何時候修改屬性時,更新此更新計數以強制更新文檔。
    • 用於存儲縮小的值的散列字段。
    • 存儲對象的屬性(未存儲在數據庫中)。
    • 「放氣」和「膨脹」即取的散列值的方法並將其轉換爲對象或採取的對象並將其轉換爲哈希值。
  • 創建於堅持,更新和負載監聽監聽器。這些監聽器應該「膨脹」和「放氣」擴展AbstractAttributeAware類的任何文檔。
  • 創建一個類作爲處理通貨膨脹和通貨緊縮的「AttributeHandler」。基本上是一個容納充氣和放氣功能的類。我想你可以把它們放在你的聽衆中,而不是你想要的。

對不起,我不能放下更多,但如果需要進一步澄清,我很樂意提供幫助。關鍵是以粗體顯示的updateCount字段(讓我永遠走到那個底部)。

0

做了很多的後研究我想出了以下解決方案。希望它可以幫助.... 1 services.yml註冊事件偵聽器

服務: my_doctrine_listener: 類:測試\ SamplerestBundle \監聽\ ModifyColumn 標籤: - {名稱:doctrine_mongodb.odm。 event_listener,事件:loadClassMetadata}

  • 在試驗\ SamplerestBundle \監聽\ ModifyColumn添加以下方法
  • 公共函數loadClassMetadata(LoadClassMetadataEventArgs $ EV entArgs) { $ classMetadata = $ eventArgs-> getClassMetadata(); $ document =(string)$ classMetadata-> getName();

    if($document == "TestBundle\\Document\\TestDocument"){ 
         $fieldMapping = array(
          'fieldName' => 'columnTest', 
          'type' => 'string' 
         ); 
    
         /* 
         the field is registered as private method 
         */ 
         $classMetadata->reflFields['columnTest'] = new \ReflectionProperty($classMetadata->name,'about'); 
         $classMetadata->mapField($fieldMapping); 
        } 
    
    } 
    
  • 添加魔術方法在文獻

    公共函數__set($柱,$值){$ 這 - > $柱= $值; }

    public function __get($ column){ return $ this - > $ column; }

  • //試試吧!

    相關問題