19declare(strict_types=1);
39 $this->tree = $a_tree;
40 $this->db =
$DIC->database();
54 $query =
'SELECT s.child FROM ' .
55 $this->
getTree()->getTreeTable() .
' s, ' .
56 $this->
getTree()->getTreeTable() .
' t ' .
57 'WHERE t.child = %s ' .
58 'AND s.lft > t.lft ' .
59 'AND s.rgt < t.rgt ' .
60 'AND s.' . $this->
getTree()->getTreePk() .
' = %s';
62 $res = $this->db->queryF(
64 array(
'integer',
'integer'),
65 array($a_node_id, $this->
getTree()->getTreeId())
69 $childs[] = (
int) $row->child;
80 bool $a_force_join_reference =
true,
84 if (is_array($a_types)) {
86 $type_str =
"AND " . $this->db->in(
87 $this->
getTree()->getObjectDataTable() .
".type",
96 if ($type_str || $a_force_join_reference) {
97 $join = $this->
getTree()->buildJoin();
101 if (count($a_fields)) {
102 $fields = implode(
',', $a_fields);
107 "FROM " . $this->
getTree()->getTreeTable() .
" " .
109 "WHERE " . $this->
getTree()->getTreeTable() .
'.lft ' .
110 'BETWEEN ' . $this->db->quote($a_node[
'lft'],
'integer') .
' ' .
111 'AND ' . $this->db->quote($a_node[
'rgt'],
'integer') .
' ' .
112 "AND " . $this->
getTree()->getTreeTable() .
"." . $this->
getTree()->getTreePk() .
' < 0 ' .
114 "ORDER BY " . $this->
getTree()->getTreeTable() .
".lft";
125 bool $a_force_join_reference =
true,
129 if (count($a_types)) {
131 $type_str =
"AND " . $this->db->in(
132 $this->getTree()->getObjectDataTable() .
".type",
142 if ($type_str || $a_force_join_reference) {
143 $join = $this->getTree()->buildJoin();
147 if (count($a_fields)) {
148 $fields = implode(
',', $a_fields);
153 "FROM " . $this->getTree()->getTreeTable() .
" " .
155 "WHERE " . $this->getTree()->getTreeTable() .
'.lft ' .
156 'BETWEEN ' . $this->db->quote($a_node[
'lft'],
'integer') .
' ' .
157 'AND ' . $this->db->quote($a_node[
'rgt'],
'integer') .
' ' .
158 "AND " . $this->getTree()->getTreeTable() .
"." . $this->getTree()->getTreePk() .
" = " . $this->db->quote(
159 $this->getTree()->getTreeId(),
163 "ORDER BY " . $this->getTree()->getTreeTable() .
".lft";
172 if ($a_node_a === [] || $a_node_b === []) {
175 if ($a_node_a[
'child'] == $a_node_b[
'child']) {
178 if ($a_node_a[
'lft'] < $a_node_b[
'lft'] && $a_node_a[
'rgt'] > $a_node_b[
'rgt']) {
181 if ($a_node_b[
'lft'] < $a_node_a[
'lft'] && $a_node_b[
'rgt'] > $a_node_a[
'rgt']) {
186 if ($a_node_a[
'parent'] == $a_node_b[
'parent']) {
192 public function getPathIds(
int $a_endnode,
int $a_startnode = 0): array
194 return $this->getPathIdsUsingAdjacencyMap($a_endnode, $a_startnode);
200 public function insertNode(
int $a_node_id,
int $a_parent_id,
int $a_pos): void
202 $insert_node_callable =
function (
ilDBInterface $db) use ($a_node_id, $a_parent_id, $a_pos):
void {
208 'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
209 'WHERE child = %s ' .
210 'AND ' . $this->getTree()->getTreePk() .
' = %s ',
211 $this->db->quote($a_parent_id,
'integer'),
212 $this->db->quote($this->getTree()->getTreeId(),
'integer')
215 $res = $this->db->query($query);
216 $r = $this->db->fetchObject(
$res);
218 if ($r->parent ===
null) {
227 if ($this->getTree()->__isMainTree()) {
229 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
230 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
231 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END ',
232 $this->db->quote($left,
'integer'),
233 $this->db->quote($left,
'integer')
235 $res = $this->db->manipulate($query);
238 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
239 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
240 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END ' .
241 'WHERE ' . $this->getTree()->getTreePk() .
' = %s ',
242 $this->db->quote($left,
'integer'),
243 $this->db->quote($left,
'integer'),
244 $this->db->quote($this->getTree()->getTreeId(),
'integer')
246 $res = $this->db->manipulate($query);
253 if ($this->getTree()->getGap() > 0) {
256 'SELECT rgt,lft,parent FROM ' . $this->getTree()->getTreeTable() .
' ' .
257 'WHERE child = %s ' .
258 'AND ' . $this->getTree()->getTreePk() .
' = %s',
259 $this->db->quote($a_parent_id,
'integer'),
260 $this->db->quote($this->getTree()->getTreeId(),
'integer')
262 $res = $this->db->query($query);
263 $r = $this->db->fetchAssoc(
$res);
265 if ($r[
'parent'] ===
null) {
269 $parentRgt = (
int) $r[
'rgt'];
270 $parentLft = (
int) $r[
'lft'];
273 $availableSpace = $parentRgt - $parentLft;
274 if ($availableSpace < 2) {
282 if ($this->getTree()->__isMainTree()) {
284 'SELECT MAX(rgt) max_rgt FROM ' . $this->getTree()->getTreeTable() .
' ' .
285 'WHERE parent = %s ',
286 $this->db->quote($a_parent_id,
'integer')
288 $res = $this->db->query($query);
289 $r = $this->db->fetchAssoc(
$res);
292 'SELECT MAX(rgt) max_rgt FROM ' . $this->getTree()->getTreeTable() .
' ' .
293 'WHERE parent = %s ' .
294 'AND ' . $this->getTree()->getTreePk() .
' = %s',
295 $this->db->quote($a_parent_id,
'integer'),
296 $this->db->quote($this->getTree()->getTreeId(),
'integer')
298 $res = $this->db->query($query);
299 $r = $this->db->fetchAssoc(
$res);
302 if (isset($r[
'max_rgt'])) {
305 $availableSpace = $parentRgt - $r[
'max_rgt'];
306 $lft = $r[
'max_rgt'] + 1;
311 $lft = $parentLft + 1;
317 if ($availableSpace < 2) {
318 if ($this->getTree()->__isMainTree()) {
320 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
321 'lft = CASE WHEN lft > %s THEN lft + %s ELSE lft END, ' .
322 'rgt = CASE WHEN rgt >= %s THEN rgt + %s ELSE rgt END ',
323 $this->db->quote($parentRgt,
'integer'),
324 $this->db->quote((2 + $this->getTree()->getGap() * 2),
'integer'),
325 $this->db->quote($parentRgt,
'integer'),
326 $this->db->quote((2 + $this->getTree()->getGap() * 2),
'integer')
328 $res = $this->db->manipulate($query);
331 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
332 'lft = CASE WHEN lft > %s THEN lft + %s ELSE lft END, ' .
333 'rgt = CASE WHEN rgt >= %s THEN rgt + %s ELSE rgt END ' .
334 'WHERE ' . $this->getTree()->getTreePk() .
' = %s ',
335 $this->db->quote($parentRgt,
'integer'),
336 $this->db->quote((2 + $this->getTree()->getGap() * 2),
'integer'),
337 $this->db->quote($parentRgt,
'integer'),
338 $this->db->quote((2 + $this->getTree()->getGap() * 2),
'integer'),
339 $this->db->quote($this->getTree()->getTreeId(),
'integer')
341 $res = $this->db->manipulate($query);
347 if ($this->getTree()->__isMainTree()) {
349 'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
351 $this->db->quote($a_parent_id,
'integer')
353 $res = $this->db->query($query);
356 'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
357 'WHERE child = %s ' .
358 'AND ' . $this->getTree()->getTreePk() .
' = %s ',
359 $this->db->quote($a_parent_id,
'integer'),
360 $this->db->quote($this->getTree()->getTreeId(),
'integer')
362 $res = $this->db->query($query);
364 $r = $this->db->fetchObject(
$res);
366 if ($r->parent ===
null) {
376 if ($this->getTree()->__isMainTree()) {
378 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
379 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
380 'rgt = CASE WHEN rgt >= %s THEN rgt + 2 ELSE rgt END ',
381 $this->db->quote($right,
'integer'),
382 $this->db->quote($right,
'integer')
384 $res = $this->db->manipulate($query);
387 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
388 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
389 'rgt = CASE WHEN rgt >= %s THEN rgt + 2 ELSE rgt END ' .
390 'WHERE ' . $this->getTree()->getTreePk() .
' = %s',
391 $this->db->quote($right,
'integer'),
392 $this->db->quote($right,
'integer'),
393 $this->db->quote($this->getTree()->getTreeId(),
'integer')
395 $res = $this->db->manipulate($query);
405 'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
406 'WHERE child = %s ' .
407 'AND ' . $this->getTree()->getTreePk() .
' = %s ',
408 $this->db->quote($a_pos,
'integer'),
409 $this->db->quote($this->getTree()->getTreeId(),
'integer')
411 $res = $this->db->query($query);
412 $r = $this->db->fetchObject(
$res);
415 if ($r->parent != $a_parent_id) {
424 if ($this->getTree()->__isMainTree()) {
426 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
427 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
428 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END ',
429 $this->db->quote($right,
'integer'),
430 $this->db->quote($right,
'integer')
432 $res = $this->db->manipulate($query);
435 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
436 'lft = CASE WHEN lft > %s THEN lft + 2 ELSE lft END, ' .
437 'rgt = CASE WHEN rgt > %s THEN rgt + 2 ELSE rgt END ' .
438 'WHERE ' . $this->getTree()->getTreePk() .
' = %s',
439 $this->db->quote($right,
'integer'),
440 $this->db->quote($right,
'integer'),
441 $this->db->quote($this->getTree()->getTreeId(),
'integer')
443 $res = $this->db->manipulate($query);
450 $depth = $this->getTree()->getDepth($a_parent_id) + 1;
454 'INSERT INTO ' . $this->getTree()->getTreeTable() .
' (' . $this->getTree()->getTreePk() .
',child,parent,lft,rgt,depth) ' .
455 'VALUES (%s,%s,%s,%s,%s,%s)',
456 $this->db->quote($this->getTree()->getTreeId(),
'integer'),
457 $this->db->quote($a_node_id,
'integer'),
458 $this->db->quote($a_parent_id,
'integer'),
459 $this->db->quote($lft,
'integer'),
460 $this->db->quote($rgt,
'integer'),
461 $this->db->quote($depth,
'integer')
463 $res = $this->db->manipulate($query);
466 if ($this->getTree()->__isMainTree()) {
467 $ilAtomQuery = $this->db->buildAtomQuery();
468 $ilAtomQuery->addTableLock(
'tree');
469 $ilAtomQuery->addQueryCallable($insert_node_callable);
472 $insert_node_callable($this->db);
481 $delete_tree_callable =
function (
ilDBInterface $db) use ($a_node_id):
void {
484 $query =
'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
485 'WHERE child = ' . $this->db->quote($a_node_id,
'integer') .
' ' .
486 'AND ' . $this->getTree()->getTreePk() .
' = ' . $this->db->quote(
487 $this->getTree()->getTreeId(),
490 $res = $this->db->query($query);
499 'DELETE FROM ' . $this->getTree()->getTreeTable() .
' ' .
500 'WHERE lft BETWEEN %s AND %s ' .
501 'AND rgt BETWEEN %s AND %s ' .
502 'AND ' . $this->getTree()->getTreePk() .
' = %s',
503 $this->db->quote($node[
'lft'],
'integer'),
504 $this->db->quote($node[
'rgt'],
'integer'),
505 $this->db->quote($node[
'lft'],
'integer'),
506 $this->db->quote($node[
'rgt'],
'integer'),
507 $this->db->quote($node[$this->getTree()->getTreePk()],
'integer')
509 $res = $this->db->manipulate($query);
515 $diff = $node[
"rgt"] - $node[
"lft"] + 1;
517 $node[$this->getTree()->getTreePk()] >= 0 &&
518 $node[
'rgt'] - $node[
'lft'] >= $this->getTree()->getGap() * 2
520 if ($this->getTree()->__isMainTree()) {
522 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
523 'lft = CASE WHEN lft > %s THEN lft - %s ELSE lft END, ' .
524 'rgt = CASE WHEN rgt > %s THEN rgt - %s ELSE rgt END ',
525 $this->db->quote($node[
'lft'],
'integer'),
526 $this->db->quote($diff,
'integer'),
527 $this->db->quote($node[
'lft'],
'integer'),
528 $this->db->quote($diff,
'integer')
530 $res = $this->db->manipulate($query);
533 'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
534 'lft = CASE WHEN lft > %s THEN lft - %s ELSE lft END, ' .
535 'rgt = CASE WHEN rgt > %s THEN rgt - %s ELSE rgt END ' .
536 'WHERE ' . $this->getTree()->getTreePk() .
' = %s ',
537 $this->db->quote($node[
'lft'],
'integer'),
538 $this->db->quote($diff,
'integer'),
539 $this->db->quote($node[
'lft'],
'integer'),
540 $this->db->quote($diff,
'integer'),
541 $this->db->quote($node[$this->getTree()->getTreePk()],
'integer')
543 $res = $this->db->manipulate($query);
549 if ($this->getTree()->__isMainTree()) {
550 $ilAtomQuery = $this->db->buildAtomQuery();
551 $ilAtomQuery->addTableLock(
'tree');
552 $ilAtomQuery->addQueryCallable($delete_tree_callable);
555 $delete_tree_callable($this->db);
564 $move_to_trash_callable =
function (
ilDBInterface $db) use ($a_node_id):
void {
565 $node = $this->getTree()->getNodeTreeData($a_node_id);
567 $query =
'UPDATE ' . $this->getTree()->getTreeTable() .
' ' .
568 'SET tree = ' . $this->db->quote(-1 * $node[
'child'],
'integer') .
' ' .
569 'WHERE ' . $this->getTree()->getTreePk() .
' = ' . $this->db->quote(
570 $this->getTree()->getTreeId(),
573 'AND lft BETWEEN ' . $this->db->quote(
576 ) .
' AND ' . $this->db->quote($node[
'rgt'],
'integer') .
' ';
578 $this->db->manipulate($query);
583 if ($this->getTree()->__isMainTree()) {
584 $ilAtomQuery = $this->db->buildAtomQuery();
585 $ilAtomQuery->addTableLock(
"tree");
586 $ilAtomQuery->addQueryCallable($move_to_trash_callable);
589 $move_to_trash_callable($this->db);
606 $takeId = $a_startnode_id == 0;
608 $depth_cache = $this->getTree()->getDepthCache();
609 $parent_cache = $this->getTree()->getParentCache();
612 $this->getTree()->__isMainTree() &&
613 isset($depth_cache[$a_endnode_id]) &&
614 isset($parent_cache[$a_endnode_id])) {
615 $nodeDepth = $depth_cache[$a_endnode_id];
616 $parentId = $parent_cache[$a_endnode_id];
618 $nodeDepth = $this->getTree()->getDepth($a_endnode_id);
619 $parentId = $this->getTree()->getParentId($a_endnode_id);
628 } elseif ($nodeDepth == 1) {
629 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
631 $pathIds[] = $a_endnode_id;
633 } elseif ($nodeDepth == 2) {
634 $takeId = $takeId || $parentId == $a_startnode_id;
636 $pathIds[] = $parentId;
638 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
640 $pathIds[] = $a_endnode_id;
642 } elseif ($nodeDepth == 3) {
643 $takeId = $takeId || $this->getTree()->getRootId() == $a_startnode_id;
645 $pathIds[] = $this->getTree()->getRootId();
647 $takeId = $takeId || $parentId == $a_startnode_id;
649 $pathIds[] = $parentId;
651 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
653 $pathIds[] = $a_endnode_id;
655 } elseif ($nodeDepth < 32) {
665 $qSelect =
't1.child c0';
667 for ($i = 1; $i < $nodeDepth - 2; $i++) {
668 $qSelect .=
', t' . $i .
'.parent c' . $i;
669 if ($this->getTree()->__isMainTree()) {
670 $qJoin .=
' JOIN ' . $this->getTree()->getTreeTable() .
' t' . $i .
' ON ' .
671 't' . $i .
'.child=t' . ($i - 1) .
'.parent ';
673 $qJoin .=
' JOIN ' . $this->getTree()->getTreeTable() .
' t' . $i .
' ON ' .
674 't' . $i .
'.child=t' . ($i - 1) .
'.parent AND ' .
675 't' . $i .
'.' . $this->getTree()->getTreePk() .
' = ' . $this->getTree()->getTreeId();
679 if ($this->getTree()->__isMainTree()) {
680 $types = array(
'integer');
681 $data = array($parentId);
682 $query =
'SELECT ' . $qSelect .
' ' .
683 'FROM ' . $this->getTree()->getTreeTable() .
' t0 ' . $qJoin .
' ' .
684 'WHERE t0.child = %s ';
686 $types = array(
'integer',
'integer');
687 $data = array($this->getTree()->getTreeId(), $parentId);
688 $query =
'SELECT ' . $qSelect .
' ' .
689 'FROM ' . $this->getTree()->getTreeTable() .
' t0 ' . $qJoin .
' ' .
690 'WHERE t0.' . $this->getTree()->getTreePk() .
' = %s ' .
691 'AND t0.child = %s ';
694 $this->db->setLimit(1, 0);
695 $res = $this->db->queryF($query, $types,
$data);
697 if (
$res->numRows() == 0) {
701 $row = $this->db->fetchAssoc(
$res);
703 $takeId = $takeId || $this->getTree()->getRootId() == $a_startnode_id;
705 $pathIds[] = $this->getTree()->getRootId();
707 for ($i = $nodeDepth - 4; $i >= 0; $i--) {
708 $takeId = $takeId || $row[
'c' . $i] == $a_startnode_id;
710 $pathIds[] = (
int) $row[
'c' . $i];
713 $takeId = $takeId || $parentId == $a_startnode_id;
715 $pathIds[] = $parentId;
717 $takeId = $takeId || $a_endnode_id == $a_startnode_id;
719 $pathIds[] = $a_endnode_id;
723 return $this->getPathIdsUsingNestedSets($a_endnode_id, $a_startnode_id);
741 if ($this->getTree()->__isMainTree()) {
742 $fields = array(
'integer');
743 $data = array($a_endnode_id);
744 $query =
"SELECT T2.child " .
745 "FROM " . $this->getTree()->getTreeTable() .
" T1, " . $this->getTree()->getTreeTable() .
" T2 " .
746 "WHERE T1.child = %s " .
747 "AND T1.lft BETWEEN T2.lft AND T2.rgt " .
750 $fields = array(
'integer',
'integer',
'integer');
751 $data = array($a_endnode_id, $this->getTree()->getTreeId(), $this->getTree()->getTreeId());
752 $query =
"SELECT T2.child " .
753 "FROM " . $this->getTree()->getTreeTable() .
" T1, " . $this->getTree()->getTreeTable() .
" T2 " .
754 "WHERE T1.child = %s " .
755 "AND T1.lft BETWEEN T2.lft AND T2.rgt " .
756 "AND T1." . $this->getTree()->getTreePk() .
" = %s " .
757 "AND T2." . $this->getTree()->getTreePk() .
" = %s " .
761 $res = $this->db->queryF($query, $fields,
$data);
763 $takeId = $a_startnode_id == 0;
765 while ($row = $this->db->fetchAssoc(
$res)) {
766 if ($takeId || $row[
'child'] == $a_startnode_id) {
768 $pathIds[] = (
int) $row[
'child'];
777 public function moveTree(
int $a_source_id,
int $a_target_id,
int $a_position): void
779 $move_tree_callable =
function (
ilDBInterface $ilDB) use ($a_source_id, $a_target_id, $a_position):
void {
781 $query =
'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
782 'WHERE ( child = %s OR child = %s ) ' .
783 'AND ' . $this->getTree()->getTreePk() .
' = %s ';
784 $res = $this->db->queryF($query, array(
'integer',
'integer',
'integer'), array(
787 $this->getTree()->getTreeId()
791 if (
$res->numRows() != 2) {
793 throw new InvalidArgumentException(
'Error moving subtree');
795 $source_lft = $target_lft = $source_rgt = $target_rgt = $source_depth = $target_depth = $source_parent = 0;
796 while ($row = $this->db->fetchObject(
$res)) {
797 if ($row->child == $a_source_id) {
798 $source_lft = $row->lft;
799 $source_rgt = $row->rgt;
800 $source_depth = $row->depth;
801 $source_parent = $row->parent;
803 $target_lft = $row->lft;
804 $target_rgt = $row->rgt;
805 $target_depth = $row->depth;
810 if ($target_lft >= $source_lft && $target_rgt <= $source_rgt) {
812 throw new InvalidArgumentException(
'Error moving subtree: target is child of source');
817 $spread_diff = $source_rgt - $source_lft + 1;
818 #var_dump("<pre>","SPREAD_DIFF: ",$spread_diff,"<pre>");
820 $query =
'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
821 'lft = CASE WHEN lft > %s THEN lft + %s ELSE lft END, ' .
822 'rgt = CASE WHEN rgt >= %s THEN rgt + %s ELSE rgt END ';
824 if ($this->getTree()->__isMainTree()) {
825 $res = $this->db->manipulateF($query, array(
'integer',
'integer',
'integer',
'integer'), [
832 $query .= (
'WHERE ' . $this->getTree()->getTreePk() .
' = %s ');
833 $res = $this->db->manipulateF(
835 array(
'integer',
'integer',
'integer',
'integer',
'integer'),
841 $this->getTree()->getTreeId()
848 if ($source_lft > $target_rgt) {
849 $where_offset = $spread_diff;
850 $move_diff = $target_rgt - $source_lft - $spread_diff;
853 $move_diff = $target_rgt - $source_lft;
855 $depth_diff = $target_depth - $source_depth + 1;
857 $query =
'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
858 'parent = CASE WHEN parent = %s THEN %s ELSE parent END, ' .
861 'depth = depth + %s ' .
865 if ($this->getTree()->__isMainTree()) {
866 $res = $this->db->manipulateF(
868 array(
'integer',
'integer',
'integer',
'integer',
'integer',
'integer',
'integer'),
875 $source_lft + $where_offset,
876 $source_rgt + $where_offset
880 $query .=
'AND ' . $this->getTree()->getTreePk() .
' = %s ';
881 $res = $this->db->manipulateF(
883 array(
'integer',
'integer',
'integer',
'integer',
'integer',
'integer',
'integer',
'integer'),
890 $source_lft + $where_offset,
891 $source_rgt + $where_offset,
892 $this->getTree()->getTreeId()
898 $query =
'UPDATE ' . $this->getTree()->getTreeTable() .
' SET ' .
899 'lft = CASE WHEN lft >= %s THEN lft - %s ELSE lft END, ' .
900 'rgt = CASE WHEN rgt >= %s THEN rgt - %s ELSE rgt END ';
902 if ($this->getTree()->__isMainTree()) {
903 $res = $this->db->manipulateF($query, array(
'integer',
'integer',
'integer',
'integer'), [
904 $source_lft + $where_offset,
906 $source_rgt + $where_offset,
910 $query .= (
'WHERE ' . $this->getTree()->getTreePk() .
' = %s ');
912 $res = $this->db->manipulateF(
914 array(
'integer',
'integer',
'integer',
'integer',
'integer'),
916 $source_lft + $where_offset,
918 $source_rgt + $where_offset,
920 $this->getTree()->getTreeId()
926 if ($this->getTree()->__isMainTree()) {
927 $ilAtomQuery = $this->db->buildAtomQuery();
928 $ilAtomQuery->addTableLock(
'tree');
929 $ilAtomQuery->addQueryCallable($move_tree_callable);
932 $move_tree_callable($this->db);
942 $query =
"SELECT t2.lft lft, t2.rgt rgt, t2.child child, t2.parent parent, type " .
943 "FROM " . $this->getTree()->getTreeTable() .
" t1 " .
944 "JOIN " . $this->getTree()->getTreeTable() .
" t2 ON (t2.lft BETWEEN t1.lft AND t1.rgt) " .
945 "JOIN " . $this->getTree()->getTableReference() .
" obr ON t2.child = obr.ref_id " .
946 "JOIN " . $this->getTree()->getObjectDataTable() .
" obd ON obr.obj_id = obd.obj_id " .
947 "WHERE t1.child = " . $this->db->quote($a_endnode_id,
'integer') .
" " .
948 "AND t1." . $this->getTree()->getTreePk() .
" = " . $this->db->quote(
949 $this->getTree()->getTreeId(),
952 "AND t2." . $this->getTree()->getTreePk() .
" = " . $this->db->quote(
953 $this->getTree()->getTreeId(),
958 $res = $this->db->query($query);
961 $nodes[(
int) $row->child][
'lft'] = (
int) $row->lft;
962 $nodes[(
int) $row->child][
'rgt'] = (
int) $row->rgt;
963 $nodes[(
int) $row->child][
'child'] = (
int) $row->child;
964 $nodes[(
int) $row->child][
'parent'] = (
int) $row->parent;
965 $nodes[(
int) $row->child][
'type'] = (
string) $row->type;
977 $query =
'select ' . $this->getTree()->getTreePk() .
', child from ' . $this->getTree()->getTreeTable() .
' child where not exists ' .
979 'select child from ' . $this->getTree()->getTreeTable() .
' parent where child.parent = parent.child and (parent.lft < child.lft) and (parent.rgt > child.rgt) ' .
981 'and ' . $this->getTree()->getTreePk() .
' = ' . $this->getTree()->getTreeId() .
' and child <> 1';
982 $res = $this->db->query($query);
986 $failures[] = $row[$this->getTree()->getTreePk()];
997 if (!isset($a_node)) {
1000 throw new InvalidArgumentException(
$message);
1004 $query =
'SELECT count(*) cnt FROM ' . $this->getTree()->getTreeTable() .
' ' .
1005 $this->getTree()->buildJoin() .
1006 'WHERE lft <= %s ' .
1008 'AND parent = %s ' .
1009 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ';
1011 $res = $this->db->queryF($query, array(
'integer',
'text',
'integer',
'integer'), array(
1015 $this->getTree()->getTreeId()
1018 $query =
'SELECT count(*) cnt FROM ' . $this->getTree()->getTreeTable() .
' ' .
1019 $this->getTree()->buildJoin() .
1020 'WHERE lft <= %s ' .
1021 'AND parent = %s ' .
1022 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ';
1024 $res = $this->db->queryF($query, array(
'integer',
'integer',
'integer'), array(
1027 $this->getTree()->getTreeId()
1030 $row = $this->db->fetchAssoc(
$res);
1031 return (
int) $row[
"cnt"];
1042 $query =
'SELECT lft FROM ' . $this->getTree()->getTreeTable() .
' ' .
1043 'WHERE ' . $this->getTree()->getTreeTable() .
'.child = %s ' .
1044 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ';
1045 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1047 $this->getTree()->getTreeId()
1049 $curr_node = $this->db->fetchAssoc(
$res);
1052 $query =
'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
1053 $this->getTree()->buildJoin() .
1055 'AND ' . $this->getTree()->getObjectDataTable() .
'.type = %s ' .
1056 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ' .
1058 $this->db->setLimit(1, 0);
1059 $res = $this->db->queryF($query, array(
'integer',
'text',
'integer'), array(
1062 $this->getTree()->getTreeId()
1065 $query =
'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
1066 $this->getTree()->buildJoin() .
1068 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ' .
1070 $this->db->setLimit(1, 0);
1071 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1073 $this->getTree()->getTreeId()
1077 if (
$res->numRows() < 1) {
1080 $row = $this->db->fetchAssoc(
$res);
1081 return $this->getTree()->fetchNodeData($row);
1092 if (!isset($a_node_id)) {
1095 throw new InvalidArgumentException(
$message);
1099 $query =
'SELECT lft FROM ' . $this->getTree()->getTreeTable() .
' ' .
1100 'WHERE ' . $this->getTree()->getTreeTable() .
'.child = %s ' .
1101 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ';
1102 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1104 $this->getTree()->getTreeId()
1107 $curr_node = $this->db->fetchAssoc(
$res);
1110 $query =
'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
1111 $this->getTree()->buildJoin() .
1113 'AND ' . $this->getTree()->getObjectDataTable().
'.type = %s ' .
1114 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ' .
1115 'ORDER BY lft DESC';
1116 $this->db->setLimit(1, 0);
1117 $res = $this->db->queryF($query, array(
'integer',
'text',
'integer'), array(
1120 $this->getTree()->getTreeId()
1123 $query =
'SELECT * FROM ' . $this->getTree()->getTreeTable() .
' ' .
1124 $this->getTree()->buildJoin() .
1126 'AND ' . $this->getTree()->getTreeTable() .
'.' . $this->getTree()->getTreePk() .
' = %s ' .
1127 'ORDER BY lft DESC';
1128 $this->db->setLimit(1, 0);
1129 $res = $this->db->queryF($query, array(
'integer',
'integer'), array(
1131 $this->getTree()->getTreeId()
1135 if (
$res->numRows() < 1) {
1138 $row = $this->db->fetchAssoc(
$res);
1139 return $this->getTree()->fetchNodeData($row);
Thrown if invalid tree strucutes are found.
static getLogger(string $a_component_id)
Get component logger.
Base class for nested set path based trees.
getPathIdsUsingNestedSets(int $a_endnode_id, int $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
moveTree(int $a_source_id, int $a_target_id, int $a_position)
Move a source subtree to target.
getSubTreeIds(int $a_node_id)
Get subtree ids @retutn int[].
getTrashSubTreeQuery(array $a_node, array $a_types, bool $a_force_join_reference=true, array $a_fields=[])
Get subtree query for trashed tree items.
getSubTreeQuery(array $a_node, array $a_types=[], bool $a_force_join_reference=true, array $a_fields=[])
Get subtree.
getPathIdsUsingAdjacencyMap(int $a_endnode_id, int $a_startnode_id=0)
get path from a given startnode to a given endnode if startnode is not given the rootnode is startnod...
deleteTree(int $a_node_id)
Delete tree.
fetchSuccessorNode(int $a_node_id, string $a_type="")
get node data of successor node
getChildSequenceNumber(array $a_node, string $type="")
get sequence number of node in sibling sequence
__construct(ilTree $a_tree)
Constructor.
getPathIds(int $a_endnode, int $a_startnode=0)
Get path ids from a startnode to a given endnode.
moveToTrash(int $a_node_id)
Move subtree to trash.
getSubtreeInfo(int $a_endnode_id)
getRelation(array $a_node_a, array $a_node_b)
Get relation of two nodes.
insertNode(int $a_node_id, int $a_parent_id, int $a_pos)
validateParentRelations()
Validate the parent relations of the tree implementation For nested set, validate the lft,...
fetchPredecessorNode(int $a_node_id, string $a_type="")
get node data of predecessor node
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 materialized path.