/** * Execute moving a single node */ protected function moveNode($srcAbsPath, $dstAbsPath) { $this->assertLoggedIn(); PathHelper::assertValidAbsolutePath($srcAbsPath, false, true, $this->getNamespacePrefixes()); PathHelper::assertValidAbsolutePath($dstAbsPath, true, true, $this->getNamespacePrefixes()); if (!$this->pathExists($srcAbsPath)) { throw new PathNotFoundException("Source path '{$srcAbsPath}' not found"); } if ($this->getSystemIdForNode($dstAbsPath)) { throw new ItemExistsException("Cannot move '{$srcAbsPath}' to '{$dstAbsPath}' because destination node already exists."); } if (!$this->getSystemIdForNode(PathHelper::getParentPath($dstAbsPath))) { throw new PathNotFoundException("Parent of the destination path '" . $dstAbsPath . "' has to exist."); } $query = 'SELECT path, id FROM phpcr_nodes WHERE path LIKE ? OR path = ? AND workspace_name = ? ' . $this->getConnection()->getDatabasePlatform()->getForUpdateSQL(); $stmt = $this->getConnection()->executeQuery($query, array($srcAbsPath . '/%', $srcAbsPath, $this->workspaceName)); /* * TODO: https://github.com/jackalope/jackalope-doctrine-dbal/pull/26/files#L0R1057 * the other thing i wonder: can't you do the replacement inside sql instead of loading and then storing * the node? this will be extremely slow for a large set of nodes. i think you should use query builder here * rather than raw sql, to make it work on a maximum of platforms. * * can you try to do this please? if we don't figure out how to do it, at least fix the where criteria, and * we can ask the doctrine community how to do the substring operation. * http://stackoverflow.com/questions/8619421/correct-syntax-for-doctrine2s-query-builder-substring-helper-method */ $query = "UPDATE phpcr_nodes SET "; $updatePathCase = "path = CASE "; $updateParentCase = "parent = CASE "; $updateLocalNameCase = "local_name = CASE "; $updateSortOrderCase = "sort_order = CASE "; $updateDepthCase = "depth = CASE "; // TODO: Find a better way to do this // Calculate CAST type for CASE statement switch ($this->getConnection()->getDatabasePlatform()->getName()) { case 'pgsql': $intType = 'integer'; break; case 'mysql': $intType = 'unsigned'; break; default: $intType = 'integer'; } $i = 0; $values = $ids = array(); $srcAbsPathPattern = '/^' . preg_quote($srcAbsPath, '/') . '/'; while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { $values[':id' . $i] = $row['id']; $values[':path' . $i] = preg_replace($srcAbsPathPattern, $dstAbsPath, $row['path'], 1); $values[':parent' . $i] = PathHelper::getParentPath($values[':path' . $i]); $values[':depth' . $i] = PathHelper::getPathDepth($values[':path' . $i]); $updatePathCase .= "WHEN id = :id" . $i . " THEN :path" . $i . " "; $updateParentCase .= "WHEN id = :id" . $i . " THEN :parent" . $i . " "; $updateDepthCase .= "WHEN id = :id" . $i . " THEN CAST(:depth" . $i . " AS " . $intType . ") "; if ($srcAbsPath === $row['path']) { $values[':localname' . $i] = PathHelper::getNodeName($values[':path' . $i]); $updateLocalNameCase .= "WHEN id = :id" . $i . " THEN :localname" . $i . " "; $updateSortOrderCase .= "WHEN id = :id" . $i . " THEN (SELECT * FROM ( SELECT MAX(x.sort_order) + 1 FROM phpcr_nodes x WHERE x.parent = :parent" . $i . ") y) "; } $ids[] = $row['id']; $i++; } if (!$i) { return; } $ids = implode($ids, ','); $updateLocalNameCase .= "ELSE local_name END, "; $updateSortOrderCase .= "ELSE sort_order END "; $query .= $updatePathCase . "END, " . $updateParentCase . "END, " . $updateDepthCase . "END, " . $updateLocalNameCase . $updateSortOrderCase; $query .= "WHERE id IN (" . $ids . ")"; try { $this->getConnection()->executeUpdate($query, $values); } catch (DBALException $e) { throw new RepositoryException("Unexpected exception while moving node from {$srcAbsPath} to {$dstAbsPath}", $e->getCode(), $e); } $this->cleanIdentifierCache($srcAbsPath); }
public function addNode($workspaceName, \DOMElement $node) { $properties = $this->getAttributes($node); $uuid = isset($properties['jcr:uuid']['value'][0]) ? (string) $properties['jcr:uuid']['value'][0] : UUIDHelper::generateUUID(); $this->ids[$uuid] = $id = isset($this->expectedNodes[$uuid]) ? $this->expectedNodes[$uuid] : self::$idCounter++; $dom = new \DOMDocument('1.0', 'UTF-8'); $phpcrNode = $dom->createElement('sv:node'); foreach ($this->namespaces as $namespace => $uri) { $phpcrNode->setAttribute('xmlns:' . $namespace, $uri); } $dom->appendChild($phpcrNode); foreach ($properties as $propertyName => $propertyData) { if ($propertyName == 'jcr:uuid') { continue; } if (!isset($this->jcrTypes[$propertyData['type']])) { throw new \InvalidArgumentException('"' . $propertyData['type'] . '" is not a valid JCR type.'); } $phpcrNode->appendChild($this->createPropertyNode($workspaceName, $propertyName, $propertyData, $id, $dom, $phpcrNode)); } list($parentPath, $childPath) = $this->getPath($node); $namespace = ''; $name = $node->getAttributeNS($this->namespaces['sv'], 'name'); if (count($parts = explode(':', $name, 2)) == 2) { list($namespace, $name) = $parts; } if ($namespace == 'jcr' && $name == 'root') { $id = 1; $childPath = '/'; $parentPath = ''; $name = ''; $namespace = ''; } $this->addRow('phpcr_nodes', array('id' => $id, 'path' => $childPath, 'parent' => $parentPath, 'local_name' => $name, 'namespace' => $namespace, 'workspace_name' => $workspaceName, 'identifier' => $uuid, 'type' => $properties['jcr:primaryType']['value'][0], 'props' => $dom->saveXML(), 'depth' => PathHelper::getPathDepth($childPath), 'sort_order' => $id - 2)); return $this; }
/** * @dataProvider dataproviderPathDepth */ public function testGetPathDepth($path, $depth) { $this->assertEquals($depth, PathHelper::getPathDepth($path)); }
/** * Search for a next document. * * @param string|object $path document instance or path from which to search * @param string|object $anchor document instance or path which serves as an anchor from which to flatten the hierarchy * @param null|int $depth depth up to which to traverse down the tree when an anchor is provided * @param bool $ignoreRole if to ignore the role * @param null|string $class the class to filter by * * @return null|object */ private function searchDepthNext($path, $anchor, $depth = null, $ignoreRole = false, $class = null) { if (is_object($path)) { $path = $this->getDm()->getUnitOfWork()->getDocumentId($path); } if (null === $path || '/' === $path) { return; } $node = $this->getDm()->getPhpcrSession()->getNode($path); if (is_object($anchor)) { $anchor = $this->getDm()->getUnitOfWork()->getDocumentId($anchor); } if (0 !== strpos($path, $anchor)) { throw new \RuntimeException("The anchor path '{$anchor}' is not a parent of the current path '{$path}'."); } // take the first eligible child if there are any if (null === $depth || PathHelper::getPathDepth($path) - PathHelper::getPathDepth($anchor) < $depth) { $childNames = $node->getNodeNames()->getArrayCopy(); $result = $this->checkChildren($childNames, $path, $ignoreRole, $class); if ($result) { return $result; } } $parent = $node->getParent(); $parentPath = PathHelper::getParentPath($path); // take the first eligible sibling if (0 === strpos($parentPath, $anchor)) { $childNames = $parent->getNodeNames()->getArrayCopy(); $key = array_search($node->getName(), $childNames); $childNames = array_slice($childNames, $key + 1); $result = $this->checkChildren($childNames, $parentPath, $ignoreRole, $class); if ($result) { return $result; } } // take the first eligible parent, traverse up while ('/' !== $parentPath) { $parent = $parent->getParent(); if (false === strpos($parent->getPath(), $anchor)) { return; } $childNames = $parent->getNodeNames()->getArrayCopy(); $key = array_search(PathHelper::getNodeName($parentPath), $childNames); $childNames = array_slice($childNames, $key + 1); $parentPath = $parent->getPath(); $result = $this->checkChildren($childNames, $parentPath, $ignoreRole, $class); if ($result) { return $result; } } return; }
/** * {@inheritdoc} */ public function findParentsWithSiblingsByUuid($uuid, $locale, $webspaceKey, MappingInterface $mapping, UserInterface $user = null) { $contentPath = $this->sessionManager->getContentPath($webspaceKey); $path = $this->resolvePathByUuid($uuid); $locales = $this->getLocalesByWebspaceKey($webspaceKey); $queryBuilder = $this->getQueryBuilder($locale, $locales, $user)->orderBy($this->qomFactory->propertyValue('node', 'jcr:path'))->where($this->qomFactory->childNode('node', $path)); while (PathHelper::getPathDepth($path) > PathHelper::getPathDepth($contentPath)) { $path = PathHelper::getParentPath($path); $queryBuilder->orWhere($this->qomFactory->childNode('node', $path)); } $mapping->addProperties(['order']); $this->appendMapping($queryBuilder, $mapping, $locale, $locales); $result = $this->resolveQueryBuilder($queryBuilder, $locale, $locales, $mapping, $user); return $this->generateTreeByPath($result); }