我想更新一个有简单引用的数据库条目。 参考:
/**
* @MongoDB\ReferenceOne(targetDocument="Acme\TestBundle\Document\Reference", simple=true)
*/
protected $reference;
目前我使用此解决方案:
$document->setReference($dm->getRepository('TestBundle:Reference')->find(1));
它正在运行但我对这个解决方案非常不满意,因为我必须更新大量的数据库条目(1000+)并且我不想获取这么多的数据库条目。
我想做这样的事情:
$document->setReference(1);
但当然,Doctrine要求提供一个对象作为参考。有什么建议吗?
答案 0 :(得分:1)
有几种方法可以完成你想要做的事情。我的回答将使用以下模型:
/** @ODM\Document */
class TestDocument
{
/** @ODM\Id */
public $id;
/** @ODM\ReferenceOne(targetDocument="TestReference", simple=true) */
public $reference;
}
/** @ODM\Document */
class TestReference
{
/** @ODM\Id */
public $id;
/** @ODM\String */
public $name;
}
首先,让我们创建单独的TestDocument和TestReference文档,用DocumentManager($dm
)保存它们,然后刷新并清除:
$document = new TestDocument();
$reference = new TestReference();
$reference->name = 'foo';
$dm->persist($document);
$dm->persist($reference);
$dm->flush();
$dm->clear();
此时,我们的DocumentManager不再跟踪文档,但我们会将它们放在我们的数据库中。 TestReference上的附加$name
属性将说明ODM如何处理代理对象。
此时,我可以使用非水合查询来查看两个文档的数据库值:
$rs = $dm->createQueryBuilder(get_class($document))
->field('id')->equals($document->id)
->hydrate(false)
->getQuery()
->getSingleResult();
var_dump($rs);
$rs = $dm->createQueryBuilder(get_class($reference))
->field('id')->equals($reference->id)
->hydrate(false)
->getQuery()
->getSingleResult();
var_dump($rs);
这应该打印类似的东西(显然ObjectIds会有所不同),以下内容:
array(1) {
["_id"]=>
object(MongoId)#191 (1) {
["$id"]=>
string(24) "52e01b86e84df17c548b4569"
}
}
array(2) {
["_id"]=>
object(MongoId)#578 (1) {
["$id"]=>
string(24) "52e01b86e84df17c548b456a"
}
["name"]=>
string(3) "foo"
}
查询构建器可能是在TestDocument上设置简单引用的最简单方法:
$dm->createQueryBuilder(get_class($document))
->update()
->field('id')->equals($document->id)
->field('reference')->set($reference->id)
->getQuery()
->execute();
ODM自然会在查询构建器中负责准备条件和修饰符值。因此,即使$document->id
可能是一个字符串(ODM将MongoId对象转换为字符串以进行水合/持久化),ODM将通过真实的ObjectId匹配文档。同样,设置reference
字段。 ODM知道我们正在处理一个简单的引用,所以它不会打扰创建DBRef;但是,$reference->id
可能是一个字符串,因此需要将其转换为MongoId对象。执行此更新查询后,转储TestDocument的数据库值应报告以下内容:
array(2) {
["_id"]=>
object(MongoId)#191 (1) {
["$id"]=>
string(24) "52e01b86e84df17c548b4569"
}
["reference"]=>
object(MongoId)#578 (1) {
["$id"]=>
string(24) "52e01b86e84df17c548b456a"
}
}
如果您想在多个文档上设置相TestDocuments。此外,使用查询构建器发出写入会绕过所有ODM的更改检测和对象管理。
我们还可以对托管文档使用基本修改来实现相同的结果。这是您在原始问题中尝试执行的操作,但在准备更新时,您可能会遇到来自$in
的异常。修改托管文档时,ODM希望您始终使用对象(完整,托管实体或代理对象)来建立关系。在准备期间,ODM尝试将引用的对象转换为适当的数据库值,这意味着分别为正常和简单引用的DBRef对象或标识符值。
那么,如果您只有引用对象的标识符怎么办?您可以分别使用id
或DocumentManager::createDBRef()
来实例化代理或文档。
代理是生成的类的实例,它们扩展了您的实际模型类。它们使用标识符字段构造,并拦截除简单DocumentManager::getReference()
实现之外的任何方法调用,以便懒惰地将文档水合 1 。 ODM为ReferenceOne和ReferenceMany关系创建了这些关系。
部分引用将是您的实际模型类(不是代理)的实例,它只设置了标识符。部分引用同样适用于DocumentManager::getPartialReference()
,尽管它们之外的可用性有限。
以下示例适用于您的情况:
getId()
如果您要转储TestDocument的数据库值,您应该会找到与查询构建器解决方案相同的结果。
最后,让我们转储TestDocument对象本身。 DocumentManager::createDBRef()
类提供了一种方法,可以方便地避免转储可能位于对象中的内部ODM服务(如果您曾$document = $dm->find(get_class($document), $document->id);
// We could also use getPartialReference() here
$document->reference = $dm->getReference(get_class($reference), $reference->id);
$dm->flush();
$dm->clear();
- 编辑托管文档,您就知道我在做什么我指的是。)
Doctrine\Common\Util\Debug
这应该产生以下结果:
var_dump()
为什么$document = $dm->find(get_class($document), $document->id);
\Doctrine\Common\Util\Debug::dump($document);
的{{1}}和object(stdClass)#610 (3) {
["__CLASS__"]=>
string(39) "Doctrine\ODM\MongoDB\Tests\TestDocument"
["id"]=>
string(24) "52e01b86e84df17c548b4569"
["reference"]=>
object(stdClass)#458 (5) {
["__CLASS__"]=>
string(40) "Doctrine\ODM\MongoDB\Tests\TestReference"
["__IS_PROXY__"]=>
bool(true)
["__PROXY_INITIALIZED__"]=>
bool(false)
["id"]=>
NULL
["name"]=>
NULL
}
}
字段为空?当我们获取TestDocument时,ODM为TestReference的ReferenceOne关系创建了一个代理。在内部,该代理确实具有适当的TestReference标识符,但不是id
方法打印的。由于我们在这个类上没有任何getter方法来触发延迟加载 1 ,我将在代理对象上调用内部name
方法,该方法在reference
界面。
Debug::dump()
现在,我们应该有以下输出:
__load()
1 :Doctrine Common 2.4支持在属性访问和方法调用上进行代理初始化。 Doctrine ORM目前就是这种情况,但MongoDB ODM尚未升级为支持它。
答案 1 :(得分:0)
我不熟悉Doctrine,我不确定以下内容是否适用于此框架,但您可以通过修改架构来实现所需。
您可能希望在“参考”文档中添加“ReferenceOne_id”字段。 1000个文档将有一个字段“ref_id”:. 如果你有1对N的关系,那么“ref_id”将是一个字符串,如果你有一个N对N的关系,那么“ref_id”将是一个数组。 通过该架构更改,您可以对“Reference”集合中具有给定“ref_id”的所有文档进行一次查询。