/** * Makes the first node the parent of the second node. * @access public * @param mixed parentIdValue The string id of the node to add as a parent. * @param mixed childIdValue The string id of the child node. * @return void **/ function addParent($parentIdValue, $childIdValue) { // ** parameter validation ArgumentValidator::validate($parentIdValue, OrValidatorRule::getRule(NonzeroLengthStringValidatorRule::getRule(), IntegerValidatorRule::getRule()), true); ArgumentValidator::validate($childIdValue, OrValidatorRule::getRule(NonzeroLengthStringValidatorRule::getRule(), IntegerValidatorRule::getRule()), true); // ** end of parameter validation // get the two nodes $parent = $this->getNode($parentIdValue); $child = $this->getNode($childIdValue); // the next two calls make sure everything will go smoothly // i.e. will help to detect that $parent is not already a parent of $child $parent->getChildren(); $child->getParents(); $this->traverse($child->getId(), true, -1); // traverse fully down in order to detect cycles // get the tree nodes $parentTreeNode = $this->_tree->getNode($parentIdValue); $childTreeNode = $this->_tree->getNode($childIdValue); // make sure that we are not adding a second parent in a single-parent hierarchy if (!$this->_allowsMultipleParents) { if ($childTreeNode->getParentsCount() > 1) { throwError(new Error(HierarchyException::SINGLE_PARENT_HIERARCHY(), "HierarchyCache", true)); } } // IMPORTANT SPECIAL CASES: // A = the number of levels that the parent is cached down $A = $this->_cache[$parentIdValue][1]; // B = the number of levels that the child is cached down $B = $this->_cache[$childIdValue][1]; // if B < A-1 AND B >= 0 then cache down the child A-1 levels if ($B < $A - 1 && $B >= 0) { $this->_traverseDown($childIdValue, $A - 1); } // if A < 0 AND B >= 0 then cache down the child fully if ($A < 0 && $B >= 0) { $this->_traverseDown($childIdValue, -1); } // the special cases are symmetric when going up // A = the number of levels that the child is cached up $A = $this->_cache[$childIdValue][2]; // B = the number of levels that the parent is cached up $B = $this->_cache[$parentIdValue][2]; // if B < A-1 AND B >= 0 then cache up the parent A-1 levels if ($B < $A - 1 && $B >= 0) { $this->_traverseUp($parentIdValue, $A - 1); } // if A < 0 AND B >= 0 then cache up the parent fully if ($A < 0 && $B >= 0) { $this->_traverseUp($parentIdValue, -1); } // now add the new node as a parent // 1) update the cache $this->_tree->addNode($childTreeNode, $parentTreeNode, true); // 2) update the database if (isset($this->harmoni_db)) { if (!isset($this->addParent_stmt)) { $query = $this->harmoni_db->insert(); $query->setTable("az2_j_node_node"); $query->addRawValue("fk_hierarchy", '?'); $query->addRawValue("fk_parent", '?'); $query->addRawValue("fk_child", '?'); $this->addParent_stmt = $query->prepare(); } $this->addParent_stmt->bindValue(1, $this->_hierarchyId); $this->addParent_stmt->bindValue(2, $parentIdValue); $this->addParent_stmt->bindValue(3, $childIdValue); $this->addParent_stmt->execute(); $queryResult = $this->addParent_stmt->getResult(); } else { $dbHandler = Services::getService("DatabaseManager"); $query = new InsertQuery(); $query->setTable("az2_j_node_node"); $query->addValue("fk_hierarchy", $this->_hierarchyId); $query->addValue("fk_parent", $parentIdValue); $query->addValue("fk_child", $childIdValue); // echo "<pre>\n"; // echo MySQL_SQLGenerator::generateSQLQuery($query); // echo "</pre>\n"; $queryResult = $dbHandler->query($query, $this->_dbIndex); } if ($queryResult->getNumberOfRows() != 1) { throw new OperationFailedException($queryResult->getNumberOfRows() . " rows found. Expecting 1."); } // Update the ancestory table $this->rebuildSubtreeAncestory($child->getId()); }
/** * Traverse a Hierarchy returning information about each Node encountered. * * @param object Id $startId * @param int $mode * @param int $direction * @param int $levels * * @return object TraversalInfoIterator * * @throws object HierarchyException An exception with one of * the following messages defined in * org.osid.hierarchy.HierarchyException may be thrown: {@link * org.osid.hierarchy.HierarchyException#OPERATION_FAILED * OPERATION_FAILED}, {@link * org.osid.hierarchy.HierarchyException#PERMISSION_DENIED * PERMISSION_DENIED}, {@link * org.osid.hierarchy.HierarchyException#CONFIGURATION_ERROR * CONFIGURATION_ERROR}, {@link * org.osid.hierarchy.HierarchyException#UNIMPLEMENTED * UNIMPLEMENTED}, {@link * org.osid.hierarchy.HierarchyException#NODE_TYPE_NOT_FOUND * NODE_TYPE_NOT_FOUND}, {@link * org.osid.hierarchy.HierarchyException#UNKNOWN_TRAVERSAL_MODE * UNKNOWN_TRAVERSAL_MODE}, {@link * org.osid.hierarchy.HierarchyException#UNKNOWN_TRAVERSAL_DIRECTION * UNKNOWN_TRAVERSAL_DIRECTION} * * @access public */ function traverse(Id $startId, $mode, $direction, $levels) { // Check the arguments ArgumentValidator::validate($mode, IntegerValidatorRule::getRule()); ArgumentValidator::validate($direction, IntegerValidatorRule::getRule()); ArgumentValidator::validate($levels, IntegerValidatorRule::getRule()); if ($mode != Hierarchy::TRAVERSE_MODE_DEPTH_FIRST) { // Only depth-first traversal is supported in the current implementation. throwError(new Error(HierarchyException::UNKNOWN_TRAVERSAL_DIRECTION(), "Hierarchy", true)); } $down = $direction == Hierarchy::TRAVERSE_DIRECTION_DOWN; $result = $this->_cache->traverse($startId, $down, $levels); return $result; }