克隆Doctrine MongoDB中嵌入的对象和存储文档

时间:2014-04-03 09:35:21

标签: mongodb symfony doctrine

我想创建一种情况,其中我有两个集合,一个用于文章,另一个用于草稿。文章是一个文档,其中包含一些属性和一组名为 features 的嵌入文档。草稿是一个具有一些属性的文档,它嵌入了一篇文章文档。

为了实现这一点,我创建了一个名为BaseArticle的基础对象,Article对象和DraftArticle继承该对象。我创建了以下对象和映射:

BaseArticle

此类包含Article和DraftArticle对象的所有getter和setter方法。 BaseArticle.mongodb.yml包含:

BaseArticle:
    type: document
    inheritanceType: COLLECTION_PER_CLASS
    fields:
        id:
            id: true
        propertyOne:
            type: string
        propertyTwo:
            type: string
        features:
            embedded: true
            type: many
            targetDocument: Feature

文章

这是一个扩展BaseArticle的空PHP类。 Article.mongodb.yml包含:

Article:
  collection: article
  type: document

草案

带有getter和setter方法的基本PHP类。 Draft.mongodb.yml看起来像这样:

Draft:
    collection: drafts
    fields:
        id:
            id: true
        owner:
            type: string
            length: 255
            index: true
        status:
            type: string
            length: 255
            index: true
        data:
            embedded: true
            type: one
            targetDocument: DraftArticle

DraftArticle

这是一个扩展BaseArticle的空PHP类。 DraftArticle.mongodb.yml包含:

DraftArticle:
    type: embeddedDocument

功能

Feature.mongodb.yml包含:

Feature:
    type: embeddedDocument
    fields:
        id:
            id: true
        name:
            type: string
        value:
            type: string

我让用户编辑文章,这些编辑可以保存为草稿。创建草稿的工作流程为:http://i.stack.imgur.com/PwbMZ.png

为了能够将文章保存为嵌入文档而不是文章集合,我需要将Article对象从Article转换为DraftArticle。 PHP无法转换对象,因此我创建了一个新对象,并使用以下代码进行深层复制。

protected function cast($destination, $sourceObject)
{
    $sourceClone = clone $sourceObject;

    if (is_string($destination)) {
        $destination = new $destination();
    }
    $sourceReflection = new \ReflectionObject($sourceClone);
    $destinationReflection = new \ReflectionObject($destination);
    $sourceProperties = $sourceReflection->getProperties();
    foreach ($sourceProperties as $sourceProperty) {
        $sourceProperty->setAccessible(true);
        $name = $sourceProperty->getName();
        $value = $sourceProperty->getValue($sourceClone);
        if ($destinationReflection->hasProperty($name)) {
            $propDest = $destinationReflection->getProperty($name);
            $propDest->setAccessible(true);
            $propDest->setValue($destination, $value);
        }
        else {
            $destination->$name = $value;
        }
    }
    return $destination;
}

现在,我可以创建草稿并将文章另存为嵌入文档,而无需更改原始文档。

$draft = new Draft();

// PHP does not support user type casting. Therefore we use a custom cast function
// which creates a new object of the target type and copies all properties.
$draftArticle = $this->cast('DraftArticle', $article);

// This will unset _id
$draftArticle->clearId();

$draft->setArticle(draftArticle);

$this->entityManager->persist($draft);
$this->entityManager->flush();

在保存之前更改draftArticle中的draftArticle或功能时,也会保存更改。 但是,当我向draftArticle添加新功能时,这些功能也将存储在原始文章文档中。

这是为什么?如何通知学说完全忘记DraftArticle与其原始文章之间的任何联系?有没有更好的方法来复制/克隆文档并将其作为嵌入文档存储在另一个集合中?

1 个答案:

答案 0 :(得分:1)

我的建议含糊不清,因为我不知道我是否一直遇到与你完全相同的问题。但我有一个场景,我需要克隆"使用Doctrine ODM从一个集合到另一个集合的文档......我收到了许多令人头疼的问题,其行为与您描述的类似。我特别难以克隆包含引用文档的哈希数组,但看起来我终于让它工作了(现在,谁知道什么时候会出现更多错误)。

最后我发现有两件事有助于摆脱时髦的行为。第一件事是使用$ documentManager-> clear();在某些关键点。

在我的项目中,我初始化$ documentManager对象,然后使用全局$ documentManager通过我的控制器使用它。我发现,如果我不做$ documentManager-> clear();在某些地方,我得到了非常奇怪的行为,在某种意义上我很难解释为什么它会这样做,但这可能是因为我缺乏理解。

这是我使用克隆文件的代码。不知道它是否会帮助你......

在我的情况下,图像/文件/视频是存储在哈希数组中的引用文档..所以我做了一些特别的事情来让那些被克隆的#34;。我也一直在寻找你的解决方案......它看起来比我的清晰得多......但是我对使用ReflectionObject和entityManager的知识还不够......我希望我能理解它们的用途。现在我严格使用$ documentManager。无论如何,如果你发现这有用,并且不要介意回答一下我在做什么和做什么之间的差异......使用entityManager vs documentManager和使用ReflectionObject克隆..好吧,我当然会很感激。干杯:)

public function cloneDocument($document) {

    $classVars = self::getObjectVars();

    foreach ($classVars as $key => $value) {

        if(in_array($key, array("images", "files", "videos")))
        {
            switch($key)
            {
                case "images":

                    foreach ($document->images as $image) {
                        $this->images[] = $image;
                    }

                    break;

                case "files":

                    foreach ($document->files as $file) {
                        $this->files[] = $file;
                    }

                    break;

                case "videos":

                    foreach ($document->videos as $video) {
                        $this->videos[] = $video;
                    }

                    break;
            }
        }
        else
        {
            $this->$key = $document->$key;
        }
    }

}