2011-04-14 5 views
7

는 업데이트 : 카테고리와 포스트 사이에 참조 된 관계로 스위치가 jwage의 추천시 : http://bit.ly/gpstW9
업데이트 (2011 5월 5일) : 예쁜 일정이 버그는 락스에 문제를 만든 (Embdedded와 반대).문제 지속 중첩 중첩 된 포함 된 문서

Doctrine ODM (Git의 최신 버전)의 최신 버전을 사용하고 있습니다.

나는 세 가지 수준의 문서 (두 가지 임베디드)를 가지고 있습니다. 카테고리 -> EmbedsMany : Post-> EmbedsMany PostVersion.

PostVersion은 자동으로 Post로 처리됩니다. 새 게시물을 만들 때 사실상 새로운 PostVersion을 만듭니다.

내 문제는 Doctrine이 PostVersions와 혼동되는 것입니다. 기존 카테고리를 검색하고 새 게시물을 추가하면 새 Post의 PostVersions가 카테고리 $ posts 컬렉션의 첫 번째 게시물에 추가됩니다.

단계별 :

  1. 새로운 포스트 (Post1)를 확인하고 분류
  2. 카테고리는, 세척, 지우기
  3. 카테고리를
  4. 만들기 검색 지속 분류 Post1 추가 새로운 게시물 (게시물 2)
  5. 게시물에 카테고리를 추가하십시오
  6. 플러시

데이터베이스의이 단계에는 카테고리 1 개와 게시물 2 개가 있어야하며 각 게시물에는 PostVersion이 하나씩 있어야합니다. 그러나 실제로 발생하는 것은 하나의 카테고리, 두 개의 게시물, 첫 번째 게시물에는 두 개의 PostVersions, 두 번째 게시물에는 PostVersions이 없습니다.

요청 중에 문서 자체가 정확합니다. 데이터베이스에 계속 유지되고 싶을뿐입니다. 내가 뭘 놓치고 있니?

예상 결과 :

{ 
    "_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(); 
    } 

} 

Post.php

<?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(); 
    } 

} 

PostVersion.PHP

... xdebug로 더러워 질 시간입니다. 지금은 지연이 중첩 포함 된 문서를 지속 EventSubscriber를 작성하여 문제를 해결 작업 한 내용은

+0

나도 비슷한 일에 약간의 문제가있다. – alex

답변

1

, 그것은 다음과 같습니다

+1 흥미로운 문제 :
+0

이 적으로 교리 자체에서 해결되었습니다

<?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); } } 

+0

GitHub에서 문제가 발견 된 것을 확인했습니다. 아직 열려 있습니다. https://github.com/doctrine/mongodb-odm/pull/232 – Cobby

+0

예. 감사합니다. 그 홍보는 나를 위해 일했다. 바라기를 그들은 빨리 병합 할 것이다. –