2012-05-16 1 views
3

У меня есть отображение объекта PHP к MongoDB документа (так называемый Node) со структуройКак удалить документ из ссылочного массива документов в доктрине ODM с 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 
} 

Как отражено на приведенных выше ссылках «Node» много «» NodeItems хранится в массиве $ пунктов и ссылки «NodeItems» один «узел» аннотации. Таким образом, это двунаправленные ссылочные коллекции.

Мой вопрос заключается в том, как эффективно удалять несколько документов «NodeItem» из его коллекции (на основе массива доступных идентификаторов), так что удаленные документы NodeItem также удаляются из ссылок массива $ 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» не удаляются. Кроме того, {cascade: persist} в аннотациях, похоже, не помогает. Код реализован в системе Symfony 2

Некоторая помощь с благодарностью!

ответ

0

Каскадное поведение в ODM соблюдается только при выполнении операций UnitOfWork. MongoDB не поддерживает каскады и триггеры (пока, во всяком случае). В вашем случае, строитель запрос будет построить и выполнить запрос вроде следующего:

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

UnitOfWork не участвует вообще (нет никаких постановочных операций или промывки) и лавина вообще не срабатывает.

С другой стороны, скажем, у вас есть управляемый объект $nodeItem. Передача его в DocumentManager::remove() вызовет UnitOfWork и приведет к тому, что любые ссылочные документы, сопоставленные cascade=REMOVE или cascade=ALL, также будут удалены. Конечно, вам нужно позвонить flush(), чтобы выполнить операции в MongoDB.

Основываясь на вашем текущем коде, единственная операция, которая будет каскадирована, - DocumentManager::persist(). На практике я предполагаю, что вы создадите узел, построите и добавите несколько NodeItems к нему и сохраните узел (позволяя его элементы сохраняться автоматически).

Если NodeItems только когда-либо принадлежат к одному узлу, вы можете избежать cascade=REMOVE и просто сделать $nodeItem->getNode()->getItems()->removeElement($nodeItem) после удаления самого $nodeItem, но перед тем flush().

Кроме того, я заметил, что вы инициализируете свое поле коллекции массивом. Позже ODM собирается увлажнить это поле как пример Doctrine\ODM\MongoDB\PersistentCollection, что может привести к двусмысленности. Как наилучшая практика, вы должны инициализировать такие поля, как Doctrine\Common\Collections\ArrayCollection в вашем конструкторе. Таким образом, вы всегда можете ожидать, что они будут Collection экземпляров.

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 Events от дополнительной информации.

Смежные вопросы