4include_once
'./Services/Tree/interfaces/interface.ilTreeImplementation.php';
26 $this->tree = $a_tree;
47 $query =
'SELECT s.child FROM ' .
48 $this->
getTree()->getTreeTable() .
' s, ' .
49 $this->
getTree()->getTreeTable() .
' t ' .
50 'WHERE t.child = %s ' .
51 'AND s.lft > t.lft ' .
52 'AND s.rgt < t.rgt ' .
53 'AND s.' . $this->
getTree()->getTreePk() .
' = %s';
57 array(
'integer',
'integer'),
58 array($a_node_id,$this->
getTree()->getTreeId())
61 $childs[] =
$row->child;
63 return $childs ? $childs : array();
75 public function getSubTreeQuery($a_node, $a_types =
'', $a_force_join_reference =
true, $a_fields = array())
80 if (is_array($a_types)) {
82 $type_str =
"AND " .
$ilDB->in($this->
getTree()->getObjectDataTable() .
".type", $a_types,
false,
"text");
84 } elseif (strlen($a_types)) {
85 $type_str =
"AND " . $this->
getTree()->getObjectDataTable() .
".type = " . $ilDB->quote($a_types,
"text");
89 if ($type_str or $a_force_join_reference) {
90 $join = $this->
getTree()->buildJoin();
94 if (count($a_fields)) {
95 $fields = implode(
',', $a_fields);
100 "FROM " . $this->
getTree()->getTreeTable() .
" " .
102 "WHERE " . $this->
getTree()->getTreeTable() .
'.lft ' .
103 'BETWEEN ' . $ilDB->quote($a_node[
'lft'],
'integer') .
' ' .
104 'AND ' .
$ilDB->quote($a_node[
'rgt'],
'integer') .
' ' .
105 "AND " . $this->
getTree()->getTreeTable() .
"." . $this->
getTree()->getTreePk() .
" = " . $ilDB->quote($this->
getTree()->getTreeId(),
'integer') .
' ' .
107 "ORDER BY " . $this->
getTree()->getTreeTable() .
".lft";
109 #ilLoggerFactory::getLogger('tree')->debug('-----------------: '. $query);
123 if ($a_node_a[
'child'] == $a_node_b[
'child']) {
127 if ($a_node_a[
'lft'] < $a_node_b[
'lft'] and $a_node_a[
'rgt'] > $a_node_b[
'rgt']) {
131 if ($a_node_b[
'lft'] < $a_node_a[
'lft'] and $a_node_b[
'rgt'] > $a_node_a[
'rgt']) {
137 if ($a_node_a[
'parent'] == $a_node_b[
'parent']) {
168 $insert_node_callable =
function (
ilDBInterface $ilDB) use ($a_node_id, $a_parent_id, $a_pos) {
174 'SELECT * FROM ' . $this->
getTree()->getTreeTable() .
' ' .
175 'WHERE child = %s ' .
176 'AND ' . $this->
getTree()->getTreePk() .
' = %s ',
177 $ilDB->quote($a_parent_id,
'integer'),
178 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
184 if (
$r->parent ===
null) {
195 'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
196 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
197 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END ' .
198 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s ',
199 $ilDB->quote($left,
'integer'),
200 $ilDB->quote($left,
'integer'),
201 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
208 if ($this->
getTree()->getGap() > 0) {
211 'SELECT rgt,lft,parent FROM ' . $this->
getTree()->getTreeTable() .
' ' .
212 'WHERE child = %s ' .
213 'AND ' . $this->
getTree()->getTreePk() .
' = %s',
214 $ilDB->quote($a_parent_id,
'integer'),
215 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
220 if (
$r[
'parent'] ===
null) {
224 $parentRgt =
$r[
'rgt'];
225 $parentLft =
$r[
'lft'];
228 $availableSpace = $parentRgt - $parentLft;
229 if ($availableSpace < 2) {
238 'SELECT MAX(rgt) max_rgt FROM ' . $this->
getTree()->getTreeTable() .
' ' .
239 'WHERE parent = %s ' .
240 'AND ' . $this->
getTree()->getTreePk() .
' = %s',
241 $ilDB->quote($a_parent_id,
'integer'),
242 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
247 if (isset(
$r[
'max_rgt'])) {
250 $availableSpace = $parentRgt -
$r[
'max_rgt'];
251 $lft =
$r[
'max_rgt'] + 1;
256 $lft = $parentLft + 1;
263 if ($availableSpace < 2) {
266 'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
267 'lft = CASE WHEN lft > %s THEN lft + %s ELSE lft END, ' .
268 'rgt = CASE WHEN rgt >= %s THEN rgt + %s ELSE rgt END ' .
269 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s ',
270 $ilDB->quote($parentRgt,
'integer'),
271 $ilDB->quote((2 + $this->getTree()->getGap() * 2),
'integer'),
272 $ilDB->quote($parentRgt,
'integer'),
273 $ilDB->quote((2 + $this->getTree()->getGap() * 2),
'integer'),
274 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
284 'SELECT * FROM ' . $this->
getTree()->getTreeTable() .
' ' .
285 'WHERE child = %s ' .
286 'AND ' . $this->
getTree()->getTreePk() .
' = %s ',
287 $ilDB->quote($a_parent_id,
'integer'),
288 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
293 if (
$r->parent ===
null) {
304 'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
305 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
306 'rgt = CASE WHEN rgt >= %s THEN rgt + 2 ELSE rgt END ' .
307 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s',
308 $ilDB->quote($right,
'integer'),
309 $ilDB->quote($right,
'integer'),
310 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
321 'SELECT * FROM ' . $this->
getTree()->getTreeTable() .
' ' .
322 'WHERE child = %s ' .
323 'AND ' . $this->
getTree()->getTreePk() .
' = %s ',
324 $ilDB->quote($a_pos,
'integer'),
325 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
331 if (
$r->parent != $a_parent_id) {
342 'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
343 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
344 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END ' .
345 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s',
346 $ilDB->quote($right,
'integer'),
347 $ilDB->quote($right,
'integer'),
348 $ilDB->quote($this->getTree()->getTreeId(),
'integer')
356 $depth = $this->
getTree()->getDepth($a_parent_id) + 1;
360 'INSERT INTO ' . $this->
getTree()->getTreeTable() .
' (' . $this->
getTree()->getTreePk() .
',child,parent,lft,rgt,depth) ' .
361 'VALUES (%s,%s,%s,%s,%s,%s)',
362 $ilDB->quote($this->getTree()->getTreeId(),
'integer'),
363 $ilDB->quote($a_node_id,
'integer'),
364 $ilDB->quote($a_parent_id,
'integer'),
365 $ilDB->quote($lft,
'integer'),
366 $ilDB->quote($rgt,
'integer'),
367 $ilDB->quote($depth,
'integer')
372 if ($this->
getTree()->__isMainTree()) {
373 $ilAtomQuery =
$ilDB->buildAtomQuery();
374 $ilAtomQuery->addTableLock(
'tree');
375 $ilAtomQuery->addQueryCallable($insert_node_callable);
378 $insert_node_callable(
$ilDB);
396 $query =
'SELECT * FROM ' . $this->
getTree()->getTreeTable() .
' ' .
397 'WHERE child = ' . $ilDB->quote($a_node_id,
'integer') .
' ' .
398 'AND ' . $this->
getTree()->getTreePk() .
' = ' . $ilDB->quote($this->
getTree()->getTreeId(),
'integer');
404 'DELETE FROM ' . $this->
getTree()->getTreeTable() .
' ' .
405 'WHERE lft BETWEEN %s AND %s ' .
406 'AND rgt BETWEEN %s AND %s ' .
407 'AND ' . $this->
getTree()->getTreePk() .
' = %s',
408 $ilDB->quote($a_node[
'lft'],
'integer'),
409 $ilDB->quote($a_node[
'rgt'],
'integer'),
410 $ilDB->quote($a_node[
'lft'],
'integer'),
411 $ilDB->quote($a_node[
'rgt'],
'integer'),
412 $ilDB->quote($a_node[$this->getTree()->getTreePk()],
'integer')
420 $diff = $a_node[
"rgt"] - $a_node[
"lft"] + 1;
422 $a_node[$this->
getTree()->getTreePk()] >= 0 &&
423 $a_node[
'rgt'] - $a_node[
'lft'] >= $this->
getTree()->getGap() * 2
427 'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
428 'lft = CASE WHEN lft > %s THEN lft - %s ELSE lft END, ' .
429 'rgt = CASE WHEN rgt > %s THEN rgt - %s ELSE rgt END ' .
430 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s ',
431 $ilDB->quote($a_node[
'lft'],
'integer'),
432 $ilDB->quote($diff,
'integer'),
433 $ilDB->quote($a_node[
'lft'],
'integer'),
434 $ilDB->quote($diff,
'integer'),
435 $ilDB->quote($a_node[$this->getTree()->getTreePk()],
'integer')
443 if ($this->
getTree()->__isMainTree()) {
444 $ilAtomQuery =
$ilDB->buildAtomQuery();
445 $ilAtomQuery->addTableLock(
'tree');
446 $ilAtomQuery->addQueryCallable($delete_tree_callable);
449 $delete_tree_callable(
$ilDB);
468 $node = $this->
getTree()->getNodeTreeData($a_node_id);
470 $query =
'UPDATE ' . $this->
getTree()->getTreeTable() .
' ' .
471 'SET tree = ' . $ilDB->quote(-1 * $node[
'child'],
'integer') .
' ' .
472 'WHERE ' . $this->
getTree()->getTreePk() .
' = ' . $ilDB->quote($this->
getTree()->getTreeId(),
'integer') .
' ' .
473 'AND lft BETWEEN ' .
$ilDB->quote($node[
'lft'],
'integer') .
' AND ' .
$ilDB->quote($node[
'rgt'],
'integer') .
' ';
480 if ($this->
getTree()->__isMainTree()) {
481 $ilAtomQuery =
$ilDB->buildAtomQuery();
482 $ilAtomQuery->addTableLock(
"tree");
484 $ilAtomQuery->addQueryCallable($move_to_trash_callable);
488 $move_to_trash_callable(
$ilDB);
512 $takeId = $a_startnode_id == 0;
514 $depth_cache = $this->
getTree()->getDepthCache();
515 $parent_cache = $this->
getTree()->getParentCache();
518 $this->
getTree()->__isMainTree() &&
519 isset($depth_cache[$a_endnode_id]) &&
520 isset($parent_cache[$a_endnode_id])) {
521 $nodeDepth = $depth_cache[$a_endnode_id];
522 $parentId = $parent_cache[$a_endnode_id];
524 $nodeDepth = $this->
getTree()->getDepth($a_endnode_id);
525 $parentId = $this->
getTree()->getParentId($a_endnode_id);
534 } elseif ($nodeDepth == 1) {
535 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
537 $pathIds[] = $a_endnode_id;
539 } elseif ($nodeDepth == 2) {
540 $takeId = $takeId || $parentId == $a_startnode_id;
542 $pathIds[] = $parentId;
544 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
546 $pathIds[] = $a_endnode_id;
548 } elseif ($nodeDepth == 3) {
549 $takeId = $takeId || $this->
getTree()->getRootId() == $a_startnode_id;
551 $pathIds[] = $this->
getTree()->getRootId();
553 $takeId = $takeId || $parentId == $a_startnode_id;
555 $pathIds[] = $parentId;
557 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
559 $pathIds[] = $a_endnode_id;
561 } elseif ($nodeDepth < 32) {
571 $qSelect =
't1.child c0';
573 for (
$i = 1;
$i < $nodeDepth - 2;
$i++) {
574 $qSelect .=
', t' .
$i .
'.parent c' .
$i;
575 $qJoin .=
' JOIN ' . $this->
getTree()->getTreeTable() .
' t' .
$i .
' ON ' .
576 't' .
$i .
'.child=t' . (
$i - 1) .
'.parent AND ' .
577 't' .
$i .
'.' . $this->
getTree()->getTreePk() .
' = ' . (int) $this->
getTree()->getTreeId();
580 $types = array(
'integer',
'integer');
582 $query =
'SELECT ' . $qSelect .
' ' .
583 'FROM ' . $this->
getTree()->getTreeTable() .
' t0 ' . $qJoin .
' ' .
584 'WHERE t0.' . $this->
getTree()->getTreePk() .
' = %s ' .
585 'AND t0.child = %s ';
590 if (
$res->numRows() == 0) {
596 $takeId = $takeId || $this->
getTree()->getRootId() == $a_startnode_id;
598 $pathIds[] = $this->
getTree()->getRootId();
600 for (
$i = $nodeDepth - 4;
$i >=0;
$i--) {
601 $takeId = $takeId ||
$row[
'c' .
$i] == $a_startnode_id;
603 $pathIds[] =
$row[
'c' .
$i];
606 $takeId = $takeId || $parentId == $a_startnode_id;
608 $pathIds[] = $parentId;
610 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
612 $pathIds[] = $a_endnode_id;
639 $fields = array(
'integer',
'integer',
'integer');
642 $query =
"SELECT T2.child " .
643 "FROM " . $this->
getTree()->getTreeTable() .
" T1, " . $this->
getTree()->getTreeTable() .
" T2 " .
644 "WHERE T1.child = %s " .
645 "AND T1.lft BETWEEN T2.lft AND T2.rgt " .
646 "AND T1." . $this->
getTree()->getTreePk() .
" = %s " .
647 "AND T2." . $this->
getTree()->getTreePk() .
" = %s " .
652 $takeId = $a_startnode_id == 0;
654 if ($takeId ||
$row[
'child'] == $a_startnode_id) {
656 $pathIds[] =
$row[
'child'];
659 return $pathIds ? $pathIds : array();
671 public function moveTree($a_source_id, $a_target_id, $a_position)
675 $move_tree_callable =
function (
ilDBInterface $ilDB) use ($a_source_id, $a_target_id, $a_position) {
677 $query =
'SELECT * FROM ' . $this->
getTree()->getTreeTable() .
' ' .
678 'WHERE ( child = %s OR child = %s ) ' .
679 'AND ' . $this->
getTree()->getTreePk() .
' = %s ';
680 $res = $ilDB->queryF(
$query, array(
'integer',
'integer',
'integer'), array(
683 $this->
getTree()->getTreeId()));
686 if (
$res->numRows() != 2) {
688 throw new InvalidArgumentException(
'Error moving subtree');
691 if (
$row->child == $a_source_id) {
692 $source_lft =
$row->lft;
693 $source_rgt =
$row->rgt;
694 $source_depth =
$row->depth;
695 $source_parent =
$row->parent;
697 $target_lft =
$row->lft;
698 $target_rgt =
$row->rgt;
699 $target_depth =
$row->depth;
704 if ($target_lft >= $source_lft and $target_rgt <= $source_rgt) {
706 throw new InvalidArgumentException(
'Error moving subtree: target is child of source');
711 $spread_diff = $source_rgt - $source_lft + 1;
712 #var_dump("<pre>","SPREAD_DIFF: ",$spread_diff,"<pre>");
714 $query =
'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
715 'lft = CASE WHEN lft > %s THEN lft + %s ELSE lft END, ' .
716 'rgt = CASE WHEN rgt >= %s THEN rgt + %s ELSE rgt END ' .
717 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s ';
718 $res = $ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer',
'integer',
'integer'), array(
723 $this->
getTree()->getTreeId()));
727 if ($source_lft > $target_rgt) {
728 $where_offset = $spread_diff;
729 $move_diff = $target_rgt - $source_lft - $spread_diff;
732 $move_diff = $target_rgt - $source_lft;
734 $depth_diff = $target_depth - $source_depth + 1;
737 $query =
'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
738 'parent = CASE WHEN parent = %s THEN %s ELSE parent END, ' .
741 'depth = depth + %s ' .
744 'AND ' . $this->
getTree()->getTreePk() .
' = %s ';
745 $res = $ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer',
'integer',
'integer',
'integer',
'integer',
'integer'), array(
751 $source_lft + $where_offset,
752 $source_rgt + $where_offset,
753 $this->
getTree()->getTreeId()));
756 $query =
'UPDATE ' . $this->
getTree()->getTreeTable() .
' SET ' .
757 'lft = CASE WHEN lft >= %s THEN lft - %s ELSE lft END, ' .
758 'rgt = CASE WHEN rgt >= %s THEN rgt - %s ELSE rgt END ' .
759 'WHERE ' . $this->
getTree()->getTreePk() .
' = %s ';
761 $res = $ilDB->manipulateF(
$query, array(
'integer',
'integer',
'integer',
'integer',
'integer'), array(
762 $source_lft + $where_offset,
764 $source_rgt + $where_offset,
766 $this->
getTree()->getTreeId()));
770 if ($this->
getTree()->__isMainTree()) {
771 $ilAtomQuery =
$ilDB->buildAtomQuery();
772 $ilAtomQuery->addTableLock(
'tree');
773 $ilAtomQuery->addQueryCallable($move_tree_callable);
776 $move_tree_callable(
$ilDB);
790 $query =
"SELECT t2.lft lft, t2.rgt rgt, t2.child child, type " .
791 "FROM " . $this->
getTree()->getTreeTable() .
" t1 " .
792 "JOIN " . $this->
getTree()->getTreeTable() .
" t2 ON (t2.lft BETWEEN t1.lft AND t1.rgt) " .
793 "JOIN " . $this->
getTree()->getTableReference() .
" obr ON t2.child = obr.ref_id " .
794 "JOIN " . $this->
getTree()->getObjectDataTable() .
" obd ON obr.obj_id = obd.obj_id " .
795 "WHERE t1.child = " . $ilDB->quote($a_endnode_id,
'integer') .
" " .
796 "AND t1." . $this->
getTree()->getTreePk() .
" = " . $ilDB->quote($this->
getTree()->getTreeId(),
'integer') .
" " .
797 "AND t2." . $this->
getTree()->getTreePk() .
" = " . $ilDB->quote($this->
getTree()->getTreeId(),
'integer') .
" " .
806 $nodes[
$row->child][
'lft'] =
$row->lft;
807 $nodes[
$row->child][
'rgt'] =
$row->rgt;
808 $nodes[
$row->child][
'child']=
$row->child;
809 $nodes[
$row->child][
'type'] =
$row->type;
811 return (array) $nodes;
821 $query =
'select child from ' . $this->
getTree()->getTreeTable() .
' child where not exists ' .
823 'select child from ' . $this->
getTree()->getTreeTable() .
' parent where child.parent = parent.child and (parent.lft < child.lft) and (parent.rgt > child.rgt) ' .
825 'and ' . $this->
getTree()->getTreePk() .
' = ' . $this->
getTree()->getTreeId() .
' and child <> 1';
sprintf('%.4f', $callTime)
An exception for terminatinating execution or to throw for unit testing.
Thrown if invalid tree strucutes are found.
static getLogger($a_component_id)
Get component logger.
Base class for nested set path based trees.
moveToTrash($a_node_id)
Move to trash.
getRelation($a_node_a, $a_node_b)
Get relation.
insertNode($a_node_id, $a_parent_id, $a_pos)
Insert tree node.
getPathIds($a_endnode, $a_startnode=0)
Get path ids.
getTree()
Get tree object.
deleteTree($a_node_id)
Delete a subtree.
getSubTreeQuery($a_node, $a_types='', $a_force_join_reference=true, $a_fields=array())
Get subtree.
moveTree($a_source_id, $a_target_id, $a_position)
Move source subtree to target.
getPathIdsUsingAdjacencyMap($a_endnode_id, $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
__construct(ilTree $a_tree)
Constructor.
getSubtreeInfo($a_endnode_id)
Get rbac subtree info @global type $ilDB.
getPathIdsUsingNestedSets($a_endnode_id, $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
validateParentRelations()
Validate the parent relations of the tree implementation For nested set, validate the lft,...
getSubTreeIds($a_node_id)
Get subtree ids.
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
Interface for tree implementations Currrently nested set or materialize path.
foreach($_POST as $key=> $value) $res