3

我有一个 php 对象映射到具有结构的 mongodb 文档(称为节点)

use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;

class Node{
    /**
    * @MongoDB\Id
    */
    protected $id;

    /**
    * @MongoDB\String
    */
    protected $domain;

    /**
    * @MongoDB\ReferenceMany(targetDocument="NodeItem",cascade=     
    * {"persist"},simple="true")
    */
    protected $items = array();

    //getter and setters below
}

还有一个名为 NodeItem 的参考文档,

class NodeItem {

  /**
  * @MongoDB\Id
  */
  protected $id;

  /**
  * @MongoDB\String
  */
  protected $name;

  /**
   * @MongoDB\ReferenceOne(targetDocument="Node", cascade={"persist"},    
   *  simple="true")
   */
   protected Node;

   //setter and getters 
}

正如上面的注释所反映的,“节点”引用了存储在 $items 数组中的许多“节点项”,而“节点项”引用了一个“节点”。所以这些是双向引用的集合。

我的问题是如何有效地从其集合中删除一些'NodeItem'文档(基于可用ID的数组),以便删除的NodeItem文档也从'Node'中的$items数组引用中删除(我认为级联删除是我要的吗?)。

我写了一个函数,代码如下:

   $qb = $this->dm->createQueryBuilder('SomeBundleBundle:NodeItem');
    /*
     * deletes from NodeItem collection
     */
    foreach($NodeItemsArray as $itemId){
        $qb->remove()->field('id')->equals($itemId)->getQuery()->execute();
    }

但是上面的函数只是从 NodeItem 集合中删除了文档,而 'Node' 的 $items 数组中的关联项并没有被删除。此外,注释中的 {cascade:persist} 似乎没有帮助。代码在 Symfony 2 框架中实现

一些帮助表示赞赏!

4

2 回答 2

1

实现这一点的唯一方法是使用 onRemove 事件的侦听器。

但是@jmikola 已经提到,您必须使用 $dm->remove() 方法,而不是 QueryBuilder (因为它还不支持事件)。


所以,要删除项目

//Get the NodeItem you want in the items array and delete it:
$items = $node->getItems();
$dm->remove($items->get(2)); //Remove the third item as an example

注册事件

class CascadeNodeDeleterListener {
        public function preRemove(LifecycleEventArgs $eventArgs) {

        $odm = $eventArgs->getDocumentManager(); /* @var $odm DocumentManager */
        $object = $eventArgs->getDocument();
        if($object instanceOf NodeItem) {
                $node = $object->getNode();
                $items = $node->getItems();
                $items->removeElement($object);
                $class = $dm->getClassMetadata(get_class($node));
                $dm->getUnitOfWork()->recomputeSingleDocumentChangeSet($class, $node);
            }

        }
}

services.yml

 <service id="listener" class="CascadeNodeDeleterListener">
      <tag name="doctrine.common.event_listener" event="onRemove" />
 </service>

请参阅更多信息中的Doctrine ODM 事件。

于 2013-05-01T07:36:54.143 回答
0

ODM 中的级联行为仅受 UnitOfWork 操作的尊重。MongoDB 本身并不支持级联和触发器(无论如何)。在您的情况下,查询构建器将构造并执行如下查询:

db.node_items.remove({"_id": ObjectId("...")})

根本不涉及 UnitOfWork(没有分阶段操作或刷新),也没有触发级联。

另一方面,假设您有一个托管$nodeItem对象。将其传递给DocumentManager::remove()将调用 UnitOfWork 并导致映射的任何引用文档cascade=REMOVEcascade=ALL被删除。当然,您必须调用flush()才能在 MongoDB 中执行操作。

根据您当前的代码,将级联的唯一操作是DocumentManager::persist(). 在实践中,我假设您将创建一个节点,构造并向其添加一些 NodeItem,然后持久化该节点(允许其项目自动持久化)。

如果 NodeItems 只属于单个节点,您可能希望避免cascade=REMOVE$nodeItem->getNode()->getItems()->removeElement($nodeItem)在删除$nodeItem自身之后但在flush().

另外,我注意到您正在将您的集合字段初始化为一个数组。稍后,ODM 将把这个领域作为一个Doctrine\ODM\MongoDB\PersistentCollection实例进行水化,这可能会导致模棱两可。作为最佳实践,您应该Doctrine\Common\Collections\ArrayCollection在构造函数中初始化此类字段。这样,您总是可以期望它们成为Collection实例。

于 2012-05-16T22:18:54.240 回答