Example #1
0
 /**
  * Push all recorded changes to the backend.
  *
  * The order is important to avoid conflicts
  * 1. remove nodes
  * 2. move nodes
  * 3. add new nodes
  * 4. commit any other changes
  *
  * If transactions are enabled but we are not currently inside a
  * transaction, the session is responsible to start a transaction to make
  * sure the backend state does not get messed up in case of error.
  *
  * @return void
  */
 public function save()
 {
     if (!$this->transport instanceof WritingInterface) {
         throw new UnsupportedRepositoryOperationException('Transport does not support writing');
     }
     // TODO: adjust transport to accept lists and do a diff request instead of single requests
     /* remove nodes/properties
      *
      * deleting a node deletes the whole tree
      * we have to avoid deleting children/properties of nodes we already
      * deleted. we sort the paths and then use that to check if parent path
      * was already removed in a - comparably - cheap way
      */
     $todelete = array_keys($this->itemsRemove);
     sort($todelete);
     $last = ':';
     // anything but '/'
     foreach ($todelete as $path) {
         if (!strncmp($last, $path, strlen($last)) && $path[strlen($last)] == '/') {
             //parent path has already been removed
             continue;
         }
         if ($this->itemsRemove[$path] instanceof NodeInterface) {
             $this->transport->deleteNode($path);
         } elseif ($this->itemsRemove[$path] instanceof PropertyInterface) {
             $this->transport->deleteProperty($path);
         } else {
             throw new RepositoryException("Internal error while deleting {$path}: unknown class " . get_class($this->itemsRemove[$path]));
         }
         $last = $path;
     }
     // move nodes/properties
     foreach ($this->nodesMove as $src => $dst) {
         $this->transport->moveNode($src, $dst);
         if (isset($this->objectsByPath['Node'][$dst])) {
             // might not be set if moved again afterwards
             // move is not treated as modified, need to confirm separately
             $this->objectsByPath['Node'][$dst]->confirmSaved();
         }
     }
     // filter out sub-nodes and sub-properties since the top-most nodes that are
     // added will create all sub-nodes and sub-properties at once
     $nodesToCreate = $this->itemsAdd;
     foreach ($nodesToCreate as $path => $dummy) {
         foreach ($nodesToCreate as $path2 => $dummy) {
             if (strpos($path2, $path . '/') === 0) {
                 unset($nodesToCreate[$path2]);
             }
         }
     }
     // create new items
     foreach ($nodesToCreate as $path => $dummy) {
         $node = $this->getNodeByPath($path);
         if (!$node instanceof NodeInterface) {
             throw new RepositoryException('Internal error: Unknown type ' . get_class($node));
         }
         $this->transport->storeNode($node);
     }
     // loop through cached nodes and commit all dirty and set them to clean.
     if (isset($this->objectsByPath['Node'])) {
         foreach ($this->objectsByPath['Node'] as $path => $node) {
             if ($node->isModified()) {
                 if (!$node instanceof NodeInterface) {
                     throw new RepositoryException('Internal Error: Unknown type ' . get_class($node));
                 }
                 foreach ($node->getProperties() as $property) {
                     if ($property->isModified() || $property->isNew()) {
                         $this->transport->storeProperty($property);
                     }
                 }
                 //order nodes
                 $reorders = $node->getOrderCommands();
                 if (count($reorders) > 0) {
                     $this->transport->reorderNodes($node->getPath(), $reorders);
                 }
             }
         }
     }
     $this->transport->finishSave();
     //clear those lists before reloading the newly added nodes from backend, to avoid collisions
     $this->itemsRemove = array();
     $this->nodesMove = array();
     foreach ($this->itemsAdd as $path => $dummy) {
         if (!isset($this->objectsByPath['Node'][$path])) {
             throw new RepositoryException("Internal error: New node was not found in cache '{$path}'");
         }
         $this->objectsByPath['Node'][$path]->confirmSaved();
     }
     if (isset($this->objectsByPath['Node'])) {
         foreach ($this->objectsByPath['Node'] as $item) {
             if ($item->isModified() || $item->isMoved()) {
                 $item->confirmSaved();
             }
         }
     }
     $this->itemsAdd = array();
 }