Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/296.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 持久化嵌套文档的问题_Php_Mongodb_Doctrine Orm_Doctrine Odm - Fatal编程技术网

Php 持久化嵌套文档的问题

Php 持久化嵌套文档的问题,php,mongodb,doctrine-orm,doctrine-odm,Php,Mongodb,Doctrine Orm,Doctrine Odm,更新:非常确定这是一个bug,在Jira上引起了一个问题: 更新(2011年5月5日):根据jwage的建议,我已切换到类别和职位之间的引用关系(与embdeded相反) 我使用的是最新版本的Doctrine ODM(来自Git) 我有三个层次的文件(两个嵌入);类别->嵌入多个:发布->嵌入多个后版本 PostVersion由Post自动处理。当我写一篇新文章时,它实际上也会在引擎盖下写一篇新的文章 我的问题是,条令与后版本混淆,如果我检索一个现有类别并向其中添加一篇新文章,则新文章的后版本将

更新:非常确定这是一个bug,在Jira上引起了一个问题:
更新(2011年5月5日):根据jwage的建议,我已切换到类别和职位之间的引用关系(与embdeded相反)

我使用的是最新版本的Doctrine ODM(来自Git)

我有三个层次的文件(两个嵌入);类别->嵌入多个:发布->嵌入多个后版本

PostVersion由Post自动处理。当我写一篇新文章时,它实际上也会在引擎盖下写一篇新的文章

我的问题是,条令与后版本混淆,如果我检索一个现有类别并向其中添加一篇新文章,则新文章的后版本将添加到该类别的$posts集合中的第一篇文章中

逐步:

  • 创建新的帖子(Post1)和类别
  • 将Post1添加到类别中
  • 持久化类别、刷新、清除
  • 检索类别
  • 新建一个帖子(Post2)
  • 将Post2添加到类别
  • 冲洗
  • 在数据库的这个阶段,应该有一个类别,两个帖子,每个帖子有一个后版本。然而,实际情况是有一个类别,两个帖子,第一个帖子有两个后版本,第二个帖子没有后版本

    请求过程中的文档本身是正确的,只是希望持久化到数据库,这是错误的。我错过了什么

    预期结果:

    {
      "_id": ObjectId("4da66baa6dd08df1f6000001"),
      "name": "The Category",
      "posts": [
        {
          "_id": ObjectId("4da66baa6dd08df1f6000002"),
          "activeVersionIndex": 0,
          "versions": [
            {
              "_id": ObjectId("4da66baa6dd08df1f6000003"),
              "name": "One Post",
              "content": "One Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            }
          ]
        },
        {
          "_id": ObjectId("4da66baa6dd08df1f6000004"),
          "activeVersionIndex": 0
          "versions": [
            {
              "_id": ObjectId("4da66baa6dd08df1f6000005"),
              "name": "Two Post",
              "content": "Two Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            }
          ]
        }
      ]
    }
    
    {
      "_id": ObjectId("4da66baa6dd08df1f6000001"),
      "name": "The Category",
      "posts": [
        {
          "_id": ObjectId("4da66baa6dd08df1f6000002"),
          "activeVersionIndex": 0,
          "versions": [
            {
              "_id": ObjectId("4da66baa6dd08df1f6000003"),
              "name": "One Post",
              "content": "One Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            },
            {
              "_id": ObjectId("4da66baa6dd08df1f6000005"),
              "name": "Two Post",
              "content": "Two Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            }
          ]
        },
        {
          "_id": ObjectId("4da66baa6dd08df1f6000004"),
          "activeVersionIndex": 0
        }
      ]
    }
    
    实际结果:

    {
      "_id": ObjectId("4da66baa6dd08df1f6000001"),
      "name": "The Category",
      "posts": [
        {
          "_id": ObjectId("4da66baa6dd08df1f6000002"),
          "activeVersionIndex": 0,
          "versions": [
            {
              "_id": ObjectId("4da66baa6dd08df1f6000003"),
              "name": "One Post",
              "content": "One Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            }
          ]
        },
        {
          "_id": ObjectId("4da66baa6dd08df1f6000004"),
          "activeVersionIndex": 0
          "versions": [
            {
              "_id": ObjectId("4da66baa6dd08df1f6000005"),
              "name": "Two Post",
              "content": "Two Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            }
          ]
        }
      ]
    }
    
    {
      "_id": ObjectId("4da66baa6dd08df1f6000001"),
      "name": "The Category",
      "posts": [
        {
          "_id": ObjectId("4da66baa6dd08df1f6000002"),
          "activeVersionIndex": 0,
          "versions": [
            {
              "_id": ObjectId("4da66baa6dd08df1f6000003"),
              "name": "One Post",
              "content": "One Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            },
            {
              "_id": ObjectId("4da66baa6dd08df1f6000005"),
              "name": "Two Post",
              "content": "Two Content",
              "metaDescription": null,
              "isAutosave": false,
              "createdAt": "Thu, 14 Apr 2011 13:36:10 +1000",
              "createdBy": "Cobby"
            }
          ]
        },
        {
          "_id": ObjectId("4da66baa6dd08df1f6000004"),
          "activeVersionIndex": 0
        }
      ]
    }
    

    这是我的文件

    Category.php

    <?php
    
    namespace Documents\Blog;
    
    use Doctrine\Common\Collections\ArrayCollection;
    
    /**
     * @Document(collection="blog")
     * @HasLifecycleCallbacks
     */
    class Category
    {
    
        /**
         * @Id
         */
        private $id;
    
        /**
         * @String
         */
        private $name;
    
        /**
         * @EmbedMany(targetDocument="Documents\Blog\Post")
         */
        private $posts;
    
        public function __construct($name = null)
        {
            $this->posts = new ArrayCollection();
            $this->setName($name);
        }
    
        public function getId()    
        {
            return $this->id;
        }
    
        public function getName()
        {
            return $this->name;
        }
    
        public function setName($name)
        {
            $this->name = $name;
        }
    
        public function getPosts()
        {
            return $this->posts->toArray();
        }
    
        public function addPost(Post $post)
        {
            $this->posts->add($post);
        }
    
        public function getPost($id)
        {
            return $this->posts->filter(function($post) use($id){
                return $post->getId() === $id;
            })->first();
        }
    
    }
    
    <?php
    
    namespace Documents\Blog;
    
    use Doctrine\Common\Collections\ArrayCollection;
    
    /**
     * @EmbeddedDocument
     * @HasLifecycleCallbacks
     */
    class Post
    {
    
        /**
         * @Id
         */
        private $id;
    
        private $firstVersion;
    
        private $activeVersion;
    
        /**
         * @Int
         */
        private $activeVersionIndex;
    
        /**
         * @EmbedMany(targetDocument="Documents\Blog\PostVersion")
         */
        private $versions;
    
        static private $currentUser;
    
        private $isDirty = false;
    
        public function __construct($name = "", $content = "")
        {
            if(!self::$currentUser){
                throw new \BlogException("Cannot create a post without the current user being set");
            }
    
            $this->versions      = new ArrayCollection();
            $this->activeVersion = $this->firstVersion = new PostVersion($name, $content, self::$currentUser);
            $this->versions->add($this->firstVersion);
            $this->isDirty = true;
        }
    
        public function getId()     
        {
            return $this->id;
        }
    
        public function getFirstVersion()
        {
            return $this->firstVersion;
        }
    
        public function getActiveVersion()
        {
            return $this->activeVersion;
        }
    
        public function setName($name)
        {
            $this->_setVersionValue('name', $name);
        }
    
        public function getName()
        {
            return $this->getActiveVersion()->getName();
        }
    
        public function setContent($content)
        {
            $this->_setVersionValue('content', $content);
        }
    
        public function getContent()
        {
            return $this->getActiveVersion()->getContent();
        }
    
        public function setMetaDescription($metaDescription)
        {
            $this->_setVersionValue('metaDescription', $metaDescription);
        }
    
        public function getMetaDescription()
        {
            return $this->getActiveVersion()->getMetaDescription();
        }
    
        public function getVersions()
        {
            return $this->versions->toArray();
        }
    
        private function _setVersionValue($property, $value)
        {   
            $version = $this->activeVersion;
    
            if(!$this->isDirty){
            // not dirty, make a new version
                $version = new PostVersion($version->getName(), $version->getContent(), self::getCurrentUser());
            }
    
            $refl = new \ReflectionProperty(get_class($version), $property);
            $refl->setAccessible(true);
    
            // updated current user
            $refl->setValue($version, $value);
    
            // unset ID
            $refl = new \ReflectionProperty(get_class($version), 'id');
            $refl->setAccessible(true);
            $refl->setValue($version, null);
    
            // updated self
            if(!$this->isDirty){
                $this->activeVersion = $version;
                $this->versions->add($version);
                $this->isDirty = true;
            }
    
            // no first version, this must be the first
            if($this->versions->count() === 1){
                $this->firstVersion = $version;
            }
        }
    
        static public function setCurrentUser($user)
        {
            self::$currentUser = $user;
        }
    
        static public function getCurrentUser()
        {
            return self::$currentUser;
        }
    
        /**
         * @PostLoad
         */
        public function findFirstVersion()
        {
            $firstVersion = null;
            foreach($this->versions as $version){
                if(null === $firstVersion){
                    // first iteration, start with any version
                    $firstVersion = $version;
                    continue;
                }
    
                if($version->getCreatedAt() < $firstVersion->getCreatedAt()){
                    // current version is newer than existing version
                    $firstVersion = $version;
                }
            }
    
            if(null === $firstVersion){
                throw new \DomainException("No first version found.");
            }
    
            $this->firstVersion = $firstVersion;
        }
    
        /**
         * @PostLoad
         */
        public function findActiveVersion()
        {
            $this->activeVersion = $this->versions->get($this->activeVersionIndex);
        }
    
        /**
         * @PrePersist
         * @PreUpdate
         */
        public function doActiveVersionIndex()
        {
            $this->activeVersionIndex = $this->versions->indexOf($this->activeVersion);
            $this->isDirty = false;
        }
    
        /**
         * @PostPersist
         * @PostUpdate
         */
        public function makeClean()
        {
            $this->isDirty = false;
        }
    
        public function getCreatedBy()
        {
            return $this->getFirstVersion()->getCreatedBy();
        }
    
        public function getCreatedAt()
        {
            return $this->getFirstVersion()->getCreatedAt();
        }
    
    }
    
    <?php
    
    namespace Documents\Blog;
    
    /**
     * @EmbeddedDocument
     */
    class PostVersion
    {
    
        /**
         * @Id
         */
        private $id;
    
        /**
         * @String
         */
        private $name;
    
        /**
         * @String
         */
        private $content;
    
        /**
         * @String(nullable="true")
         */
        private $metaDescription;
    
        /**
         * @Boolean
         */
        private $isAutosave = false;
    
        /**
         * @Date
         */
        private $createdAt;
    
        /**
         * @String
         */
        private $createdBy;
    
        public function __construct($name, $content, $author)
        {
            $this->setName($name);
            $this->setContent($content);
            $this->setCreatedBy($author);
            $this->touch();
        }
    
        public function __clone()
        {
            if($this->id){
                $this->id = null;
                $this->touch();
            }
        }
    
        private function touch()
        {
            $this->createdAt = new \DateTime();
        }
    
        public function getId()     
        {
            return $this->id;
        }
    
        public function getName()
        {
            return $this->name;
        }
    
        public function setName($name)
        {
            $this->name = $name;
        }
    
        public function getContent()
        {
            return $this->content;
        }
    
        public function setContent($content)
        {
            $this->content = $content;
        }
    
        public function getIsAutosave()
        {
            return $this->isAutosave;
        }
    
        public function setIsAutosave($isAutosave)
        {
            $this->isAutosave = $isAutosave;
        }
    
        public function getCreatedAt()
        {
            return $this->createdAt;
        }
    
        public function setCreatedAt(\DateTime $createdAt)
        {
            $this->createdAt = $createdAt;
        }
    
        public function getCreatedBy()
        {
            return $this->createdBy;
        }
    
        public function setCreatedBy($createdBy)
        {
            $this->createdBy = $createdBy;
        }
    
        public function setMetaDescription($metaDescription)
        {
            $this->metaDescription = $metaDescription;
        }
    
        public function getMetaDescription()
        {
            return $this->metaDescription;
        }
    
    }
    

    目前,我已经通过创建一个EventSubscriber来解决这个问题,它延迟了持久化嵌套嵌入文档,如下所示:

    <?php
    
    namespace Application\Blog\Domain\EventSubscribers;
    
    use Application\Blog\Domain\Document\Post,
        Doctrine\ODM\MongoDB\Event\LifecycleEventArgs,
        Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
    
    /**
     * Handles delayed insert of nested embedded documents to work around Doctrine ODM bug :(
     */
    class VersionManager implements \Doctrine\Common\EventSubscriber
    {
    
        private $versions = array();
    
        /**
         * Returns an array of events this subscriber wants to listen to.
         *
         * @return array
         */
        public function getSubscribedEvents()
        {
            return array('prePersist', 'postPersist');
        }
    
        /**
         * Move versions out of Posts into temporary storage so they are flushed without versions
         *
         * @param \Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs
         * @return void
         */
        public function prePersist(LifecycleEventArgs $eventArgs)
        {
            $document = $eventArgs->getDocument();
            if($document instanceof Post){
                $dm = $eventArgs->getDocumentManager();
                $meta = $dm->getClassMetadata(get_class($document));
                $this->addVersion($meta, $document);
                $this->clearVersions($meta, $document);
            }
        }
    
        /**
         * Move the temporary versions back onto the Posts and flush
         *
         * @param \Doctrine\ODM\MongoDB\Event\LifecycleEventArgs $eventArgs
         * @return void
         */
        public function postPersist(LifecycleEventArgs $eventArgs)
        {
            $dm = $eventArgs->getDocumentManager();
            $hasChanges = count($this->versions) > 0;
    
            foreach($this->versions as $oid => $value){
                $post = $value['document'];
                $versions = $value['versions'];
                $meta = $dm->getClassMetadata(get_class($post));
                $meta->setFieldValue($post, 'versions', $versions);
                unset($this->versions[$oid]);
            }
    
            if($hasChanges){
                $dm->flush();
            }
        }
    
        /**
         * Add versions to temporary storage
         *
         * @param \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $meta
         * @param \Application\Blog\Domain\Document\Post $post
         * @return void
         */
        private function addVersion(ClassMetadata $meta, Post $post)
        {
            $this->versions[spl_object_hash($post)] = array(
                'document' => $post,
                'versions' => $meta->getFieldValue($post, 'versions')
            );
        }
    
        /**
         * Remove versions from a Post
         *
         * @param \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $meta
         * @param \Application\Blog\Domain\Document\Post $post
         * @return void
         */
        private function clearVersions(ClassMetadata $meta, Post $post)
        {
            $meta->setFieldValue($post, 'versions', null);
        }
    
    }
    

    这个问题在教义本身就解决了吗,因为我也有类似的问题。我看到你在GitHub上发现了这个问题,它仍然是开放的:是的,谢谢。那个公关为我工作。希望他们能很快合并。