Doctrine MongoDB Symfony2:没有DB Object的简单参考

时间:2013-12-19 20:37:08

标签: php mongodb symfony reference doctrine

我想更新一个有简单引用的数据库条目。 参考:

/**
 * @MongoDB\ReferenceOne(targetDocument="Acme\TestBundle\Document\Reference", simple=true)
 */
protected $reference;

目前我使用此解决方案:

$document->setReference($dm->getRepository('TestBundle:Reference')->find(1));

它正在运行但我对这个解决方案非常不满意,因为我必须更新大量的数据库条目(1000+)并且我不想获取这么多的数据库条目。

我想做这样的事情:

$document->setReference(1);

但当然,Doctrine要求提供一个对象作为参考。有什么建议吗?

2 个答案:

答案 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对象或标识符值。

那么,如果您只有引用对象的标识符怎么办?您可以分别使用idDocumentManager::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”的所有文档进行一次查询。