2012-11-06 38 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; }

  • //试试吧!

    相关问题