我的网站记录可以包含任意数量的具有不同信息的AdProvider配置字段。不幸的是,fieldNames(提供商的名称)是独一无二的,还会有更多。我可以在Document中将它们中的每一个硬编码为哈希类型,但每次添加新的Provider时我都必须更新Document。
我想动态修改Document本身,查看我可以从另一个Mongo集合中获取的Providers列表,但我无法弄清楚如何执行此操作。
我的第一次尝试是在loadClassMetaData事件上创建一个监听器并映射新字段。我正在看到字段映射,但它们没有反映在文档中。显然这些字段没有任何getter和setter,所以我尝试使用magic __get和__set方法访问它们,但是我得到它们不存在的错误。
也许我会以错误的方式解决这个问题?
示例Mongo记录:
{
"_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 :(得分:4)
你不能用普通的Doctrine-odm做到这一点,但是我用一个简单的技巧做了几乎相同的事情:
由于可以嵌套MongodDB文档,只需在文档中创建一个@Hash
属性,该属性将包含无模式属性(将其视为散列包):
class Doc {
/**
* Schema-less dynamic properties goes here:
* @MongoDB\Hash
*/
public $extraFields;
/** @MongoDB\Date */
public $createdDate;
//Other static properties...
}
唯一需要注意的是,无架构属性将位于$extraFields
属性中,而不是位于根目录中。
然而,这只是一个美学问题,你可以在这些领域做任何事情(索引,查询,地图缩小,ecc)
通过这种方式,您可以拥有静态定义的属性(如Id,createdDate,ecc) 和一个动态属性的哈希包。
如果您想记录某些动态属性的存在(并避免给同事发生常见的拼写错误问题),您可以在Doc中定义一些访问方法:
class Doc {
public getExtraName() {
if(empty($this->extraFields['name'])) return null;
return $this->extraFields['name'];
}
}
答案 1 :(得分:1)
我为Symfony2和Doctrine2-ODM编写了一个广泛的EAV系统,其中任何文档都可以变成传统字段和属性的“混合”。这些属性都在具有特定类型(地址,电话号码,字符串,文档参考等)的另一个文档中定义。我无法共享代码,因为它是公司属性,但它的要点如下。
对不起,我不能放下更多,但如果需要进一步澄清,我很乐意提供帮助。关键是以粗体显示的updateCount字段(让我永远地到达那个底部)。
答案 2 :(得分:0)
经过大量研究后,我提出了以下解决方案。希望它可以帮助.... 1.在services.yml中注册一个事件监听器
服务: my_doctrine_listener: class:Test \ SamplerestBundle \ Listener \ ModifyColumn 标签: - {name:doctrine_mongodb.odm.event_listener,event:loadClassMetadata}
public function loadClassMetadata(LoadClassMetadataEventArgs $ eventArgs) { $ classMetadata = $ eventArgs-&gt; getClassMetadata(); $ document =(string)$ classMetadata-&gt; 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);
}
}
在文档中添加魔术方法
public function __set($column,$value){
$this->$column = $value;
}
public function __get($column){
return $this->$column;
}