File Coverage

lib/DBIx/Class/Tree/NestedSet.pm
Criterion Covered Total %
statement 293 327 89.6
branch 60 80 75.0
condition 21 29 72.4
subroutine 41 47 87.2
pod 26 32 81.2
total 441 515 85.6


line stmt bran cond sub pod time code
1             package DBIx::Class::Tree::NestedSet;
2              
3 11     11   1377406 use strict;
  11         27  
  11         278  
4 11     11   54 use warnings;
  11         26  
  11         290  
5              
6 11     11   62 use Carp qw/croak/;
  11         29  
  11         479  
7 11     11   59 use base 'DBIx::Class';
  11         26  
  11         39435  
8              
9             our $VERSION = '0.10';
10             $VERSION = eval $VERSION;
11              
12             __PACKAGE__->mk_classdata( _tree_columns => {} );
13              
14             # specify the tree columns and define the relationships
15             #
16             sub tree_columns {
17 1208     1208 1 875689 my ($class, $args) = @_;
18              
19 1208 100       3109 if (defined $args) {
20              
21             my ($root, $left, $right, $level) = map {
22 21         64 my $col = $args->{"${_}_column"};
  81         184  
23 81 100       219 croak("required param $_ not specified") if !defined $col;
24 80         190 $col;
25             } qw/root left right level/;
26              
27 20         149 my $table = $class->table;
28 20         876 my %join_cond = ( "foreign.$root" => "self.$root" );
29              
30 20         242 $class->belongs_to(
31             'root' => $class,
32             \%join_cond,{
33             where => \"me.$left = 1", #"
34             },
35             );
36              
37 20         14517 $class->belongs_to(
38             'parent' => $class,
39             \%join_cond,{
40             where => \"child.$left > me.$left AND child.$right < me.$right AND me.$level = child.$level - 1", #"
41             from => "$table me, $table child",
42             },
43             );
44              
45 20         6691 $class->has_many(
46             'nodes' => $class,
47             \%join_cond,{
48             order_by => "me.$left",
49             cascade_delete => 0,
50             },
51             );
52              
53 20         9015 $class->has_many(
54             'descendants' => $class,
55             \%join_cond, {
56             where => \"me.$left > parent.$left AND me.$right < parent.$right", #"
57             order_by => "me.$left",
58             from => "$table me, $table parent",
59             cascade_delete => 0,
60             },
61             );
62              
63 20         9076 $class->has_many(
64             'children' => $class,
65             \%join_cond, {
66             where => \"me.$left > parent.$left AND me.$right < parent.$right AND me.$level = parent.$level + 1", #"
67             order_by => "me.$left",
68             from => "$table me, $table parent",
69             cascade_delete => 0,
70             },
71             );
72              
73 20         9101 $class->has_many(
74             'ancestors' => $class,
75             \%join_cond, {
76             where => \"child.$left > me.$left AND child.$right < me.$right", #"
77             order_by => "me.$right",
78             from => "$table me, $table child",
79             cascade_delete => 0,
80             },
81             );
82              
83 20         9012 $class->_tree_columns($args);
84             }
85              
86 1207         21557 return $class->_tree_columns;
87             }
88              
89             # Insert a new node.
90             #
91             # If the 'right' column is not defined it assumes that we are inserting a root
92             # node.
93             #
94             sub insert {
95 98     98 0 4706403 my ($self, @args) = @_;
96              
97 98         470 my ($root, $left, $right, $level) = $self->_get_columns;
98              
99 98 100       1842 if (!$self->$right) {
100 17         849 $self->set_columns({
101             $left => 1,
102             $right => 2,
103             $level => 0,
104             });
105             }
106              
107 98         9054 my $row;
108 98         467 my $get_row = $self->next::can;
109             $self->result_source->schema->txn_do(sub {
110 98     98   47588 $row = $get_row->($self, @args);
111              
112             # If the root column is not defined, it uses the primary key so long as it is a
113             # single column primary key
114 98 100       177697 if (!defined $row->$root) {
115 4         121 my @primary_columns = $row->result_source->primary_columns;
116 4 100       54 if (scalar @primary_columns > 1) {
117 1         17 croak('Only single column primary keys are supported for default root selection in nested set tree classes');
118             }
119              
120             $row->update({
121 3         46 $root => \"$primary_columns[0]", #"
122             });
123              
124 3         5079 $row->discard_changes;
125             }
126 98         1168 });
127              
128 97         39706 return $row;
129             }
130              
131             # Delete the current node, and all sub-nodes.
132             #
133             sub delete {
134 6     6 1 19679 my ($self) = shift;
135              
136 6         31 my ($root, $left, $right, $level) = $self->_get_columns;
137              
138 6         110 my $p_lft = $self->$left;
139 6         161 my $p_rgt = $self->$right;
140              
141 6         85 my $del_row = $self->next::can;
142             $self->result_source->schema->txn_do(sub {
143 6     6   2143 $self->discard_changes;
144              
145 6         17935 my $descendants = $self->descendants;
146 6         10806 while (my $descendant = $descendants->next) {
147 2         3504 $del_row->($descendant);
148             }
149              
150             $self->nodes_rs->update({
151 6         8451 $left => \"CASE WHEN $left > $p_rgt THEN $left - 2 ELSE $left END", #"
152             $right => \"CASE WHEN $right > $p_rgt THEN $right - 2 ELSE $right END", #"
153             });
154 6         14815 $del_row->($self);
155 6         63 });
156             }
157              
158             # Create a related node with special handling for relationships
159             #
160             sub create_related {
161 67     67 0 12324 my ($self, $rel, $col_data) = @_;
162              
163 67 50       225 if (! grep {$rel eq $_} qw(descendants children nodes ancestors)) {
  268         755  
164 0         0 return $self->next::method($rel => $col_data);
165             }
166              
167 67         254 my ($root, $left, $right, $level) = $self->_get_columns;
168              
169 67         177 my $row;
170 67         265 my $get_row = $self->next::can;
171             $self->result_source->schema->txn_do(sub {
172 67     67   27125 $self->discard_changes;
173              
174             # With create related ancestor, make it a parent of this child
175 67 100       251363 if ($rel eq 'ancestors') {
176 2         51 my $p_lft = $self->$left;
177 2         57 my $p_rgt = $self->$right;
178 2         51 my $p_level = $self->$level;
179              
180             # Update all the nodes to the right of this sub-tree
181 2         47 $self->nodes_rs->update({
182             $left => \"CASE WHEN $left > $p_rgt THEN $left + 2 ELSE $left END", #"
183             $right => \"CASE WHEN $right > $p_rgt THEN $right + 2 ELSE $right END", #"
184             });
185              
186             # Update all the nodes of this sub-tree
187 2         5052 $self->nodes_rs->search({
188             $left => { '>=', $p_lft },
189             $right => { '<=', $p_rgt }
190             })->update({
191             $left => \"$left + 1", #"
192             $right => \"$right + 1", #"
193             $level => \"$level + 1", #"
194             });
195              
196 2         4797 $self->discard_changes;
197 2         6478 $col_data->{$root} = $self->$root;
198 2         30 $col_data->{$left} = $p_lft;
199 2         5 $col_data->{$right} = $p_rgt+2;
200 2         5 $col_data->{$level} = $p_level;
201             }
202             else {
203             # insert a descendant, node or a child as a right-most child
204 65         1760 my $p_rgt = $self->$right;
205              
206             # Update all the nodes to the right of this sub-tree
207 65         1964 $self->nodes_rs->update({
208             $left => \"CASE WHEN $left > $p_rgt THEN $left + 2 ELSE $left END", #"
209             $right => \"CASE WHEN $right >= $p_rgt THEN $right + 2 ELSE $right END", #"
210             });
211 65         181466 $self->discard_changes;
212 65         217168 $col_data->{$root} = $self->$root;
213 65         1072 $col_data->{$left} = $p_rgt;
214 65         194 $col_data->{$right} = $p_rgt+1;
215 65         1257 $col_data->{$level} = $self->$level+1;
216              
217             }
218 67         1079 $row = $get_row->($self, $rel => $col_data);
219 67         848 });
220              
221 67         15006 return $row;
222             }
223              
224             # search_related with special handling for relationships
225             #
226             sub search_related {
227 575     575 0 4611934 my ($self, $rel, $cond, @rest) = @_;
228 575         3112 my $pk = ($self->result_source->primary_columns)[0];
229              
230 575   100     12662 $cond ||= {};
231 575 100 100     4064 if ($rel eq 'descendants' || $rel eq 'children') {
    100 100        
232 380         7901 $cond->{"parent.$pk"} = $self->$pk,
233             }
234             elsif ($rel eq 'ancestors' || $rel eq 'parent') {
235 40         826 $cond->{"child.$pk"} = $self->$pk,
236             }
237              
238 575         8257 return $self->next::method($rel, $cond, @rest);
239             }
240             *search_related_rs = \&search_related;
241              
242             # Insert a node anywhere in the tree
243             # left
244             # right
245             # level
246             # other_args
247             #
248             sub _insert_node {
249 4     4   259 my ($self, $args) = @_;
250 4         16 my $rset = $self->result_source->resultset;
251 4         1030 my $schema = $self->result_source->schema;
252              
253 4         48 my ($root, $left, $right, $level) = $self->_get_columns;
254              
255             # our special arguments
256 4         11 my $o_args = delete $args->{other_args};
257 4         8 my $pivot = $args->{$left};
258              
259             # Use same level as self by default
260 4 50       13 $args->{$level} = $self->$level unless defined $args->{$level};
261 4 50       74 $args->{$root} = $self->$root unless defined $args->{$root};
262              
263             # make room and create it
264 4         52 my $new_record;
265             $schema->txn_do(sub {
266 4     4   1305 $self->discard_changes;
267 4         12294 $rset->search({
268             "me.$right" => {'>=', $pivot},
269             $root => $self->$root,
270             })->update({
271             $right => \"$right + 2", #"
272             });
273              
274 4         6374 $rset->search({
275             "me.$left" => {'>=', $pivot},
276             $root => $self->$root,
277             })->update({
278             $left => \"$left + 2", #"
279             });
280 4         5880 $self->discard_changes;
281              
282 4         11918 $new_record = $rset->create({%$o_args, %$args});
283 4         27 });
284 4         589 return $new_record;
285             }
286              
287             # Attach a node anywhere in the tree
288             # node
289             # left_delta (relative to $self->$left
290             # (or) right_delta (relative to $self->$right
291             # level
292             #
293             sub _attach_node {
294 13     13   56770 my ($self, $node, $args) = @_;
295 13         57 my $rset = $self->result_source->resultset;
296 13         3941 my $schema = $self->result_source->schema;
297 13         170 my ($root, $left, $right, $level) = $self->_get_columns;
298              
299             # $self cannot be a descendant of $node or $node itself
300 13 100 66     281 if ($self->$root == $node->$root && $self->$left >= $node->$left && $self->$right <= $node->$right) {
      100        
301 2         293 croak("Cannot _attach_node to it's own descendant ");
302             }
303              
304             $schema->txn_do(sub {
305 11     11   4367 $self->discard_changes;
306 11         36880 $node->discard_changes;
307             # Move the node to the end (right most child of root)
308 11         34390 $node->_move_to_end;
309 11         26139 $self->discard_changes;
310 11         33911 $node->discard_changes;
311             # Graft the node to the specified location
312 11         34368 my $left_val;
313 11 100       53 if (defined $args->{left_delta}) {
314 6         171 $left_val = $self->$left + $args->{left_delta};
315             }
316             else {
317 5         131 $left_val = $self->$right + $args->{right_delta};
318             }
319             $self->_graft_branch({
320             node => $node,
321             $left => $left_val,
322 11         249 $level => $args->{$level}
323             });
324 11         1286 });
325             }
326              
327              
328             # Graft a branch of nodes (or a leaf) at this point
329             # The assumption made here is that the nodes being moved here are
330             # either a root node of another tree or the rightmost child of
331             # this or another trees root (see _move_to_end)
332             #
333             sub _graft_branch {
334 13     13   12180 my ($self, $args) = @_;
335              
336 13         65 my ($root, $left, $right, $level) = $self->_get_columns;
337 13         63 my $rset = $self->result_source->resultset;
338              
339 13         3715 my $node = $args->{node};
340 13         46 my $arg_left = $args->{$left};
341 13         33 my $arg_level = $args->{$level};
342 13         52 my $node_is_root = $node->is_root;
343 13         231 my $node_root = $node->root;
344              
345 13 100       45367 if ($node_is_root) {
346             # Cannot graft our own root
347 1 50       19 croak "Cannot graft our own root node!" if $node->$root == $self->$root;
348             }
349             else {
350             # Node must be rightmost child of it's root
351 12 50       232 croak "Can only graft rightmost child of root!" if $node->$right + 1 != $node_root->$right;
352             }
353              
354             # If the position we are grafting to is the rightmost child of root then there is nothing to do
355 13 100 100     680 if ($self->$root == $node->$root && $self->is_root && $self->$left + $arg_left > $node_root->$right) {
      100        
356 2         78 return;
357             }
358              
359             # Determine the size of the branch to add in.
360 11         257 my $offset = $node->$right + 1 - $node->$left;
361              
362             # Make a hole in the tree to accept the graft
363 11         428 $self->discard_changes;
364 11         35271 $rset->search({
365             "me.$right" => {'>=', $arg_left},
366             $root => $self->$root,
367             })->update({
368             $right => \"$right + $offset", #"
369             });
370 11         18735 $rset->search({
371             "me.$left" => {'>=', $arg_left},
372             $root => $self->$root,
373             })->update({
374             $left => \"$left + $offset", #"
375             });
376              
377             # make the graft
378 11         17388 $node->discard_changes;
379 11         36361 my $node_left = $node->$left;
380 11         349 my $node_right = $node->$right;
381 11         336 my $level_offset= $arg_level - $node->$level;
382 11         287 my $graft_offset= $arg_left - $node->$left;
383              
384 11         157 $self->discard_changes;
385 11         34433 $rset->search({
386             "me.$left" => {'>=', $node_left},
387             "me.$right" => {'<=', $node_right},
388             $root => $node->$root,
389             })->update({
390             $left => \"$left + $graft_offset", #"
391             $right => \"$right + $graft_offset", #"
392             $level => \"$level + $level_offset", #"
393             $root => $self->$root,
394             });
395              
396             # adjust the right value of the root node to take into account the
397             # moved nodes
398 11 100       24283 if (! $node_is_root) {
399 10         301 $node_root->discard_changes;
400 10         31557 $node_root->$right($node_root->$right - $offset);
401 10         2721 $node_root->update;
402             }
403              
404 11         9544 $self->discard_changes;
405 11         34681 $node->discard_changes;
406             }
407              
408             # Move nodes to end of tree
409             # This will help make it easier to prune the nodes from
410             # the tree since there will be nothing to the right of them
411             #
412             sub _move_to_end {
413 16     16   62239 my ($self) = @_;
414              
415 16         76 my ($root, $left, $right, $level) = $self->_get_columns;
416 16         100 my $rset = $self->result_source->resultset;
417              
418 16         4768 my $root_node = $self->root;
419 16         59871 my $old_left = $self->$left;
420 16         491 my $old_right = $self->$right;
421 16         417 my $offset = $root_node->$right - $self->$left;
422 16         756 my $level_offset= $self->$level - 1;
423              
424             # If it is the root or already on the right, do nothing
425 16 100 66     252 if ($self->is_root || $old_right + 1 == $root_node->$right) {
426 4         67 return;
427             }
428              
429             # Move all sub-nodes to the right (adjusting their level)
430 12         220 $self->discard_changes;
431 12         40078 $rset->search({
432             "me.$left" => {'>=', $old_left},
433             "me.$right" => {'<=', $old_right},
434             $root => $self->$root,
435             })->update({
436             $left => \"$left + $offset", #"
437             $right => \"$right + $offset", #"
438             $level => \"$level - $level_offset", #"
439             });
440              
441             # Now move everything (except the root) back to fill in the gap
442 12         26732 $offset = $self->$right + 1 - $self->$left;
443 12         1218 $rset->search({
444             "me.$right" => {'>=', $old_right},
445             $left => {'!=', 1}, # Root needs no adjustment
446             $root => $self->$root,
447             })->update({
448             $right => \"$right - $offset", #"
449             });
450 12         24178 $rset->search({
451             "me.$left" => {'>=', $old_right},
452             $root => $self->$root,
453             })->update({
454             $left => \"$left - $offset", #"
455             });
456 12         20926 $self->discard_changes;
457             }
458              
459             # Convenience routine to get the names of the table columns
460             #
461             sub _get_columns {
462 271     271   670 my ($self) = @_;
463              
464             my ($root, $left, $right, $level) = map {
465 271         666 $self->tree_columns->{"${_}_column"}
  1084         20247  
466             } qw/root left right level/;
467              
468 271         6306 return ($root, $left, $right, $level);
469             }
470              
471             # Attach a node as the rightmost child of the current node
472             #
473             sub attach_rightmost_child {
474 2     2 1 19092 my $self = shift;
475              
476 2         15 my ($root, $left, $right, $level) = $self->_get_columns;
477              
478 2         7 foreach my $node (@_) {
479 2         40 $self->_attach_node($node, {
480             right_delta => 0,
481             $level => $self->$level + 1,
482             });
483             }
484 2         3452 return $self;
485             }
486             *append_child = \&attach_rightmost_child;
487              
488             # Attach a node as the leftmost child of the current node
489             #
490             sub attach_leftmost_child {
491 0     0 1 0 my $self = shift;
492              
493 0         0 my ($root, $left, $right, $level) = $self->_get_columns;
494              
495 0         0 foreach my $node (@_) {
496 0         0 $self->_attach_node($node, {
497             left_delta => 1,
498             $level => $self->$level + 1,
499             });
500             }
501 0         0 return $self;
502             }
503             *prepend_child = \&attach_leftmost_child;
504              
505             # Attach a node as a sibling to the right of self
506             #
507             sub attach_right_sibling {
508 3     3 1 12825 my $self = shift;
509              
510 3         15 my ($root, $left, $right, $level) = $self->_get_columns;
511              
512 3         12 foreach my $node (@_) {
513 3         66 $self->_attach_node($node, {
514             right_delta => 1,
515             $level => $self->$level,
516             });
517             }
518 3         9703 return $self;
519             }
520             *attach_after = \&attach_right_sibling;
521              
522             # Attach a node as a sibling to the left of self
523             #
524             sub attach_left_sibling {
525 2     2 1 4 my $self = shift;
526              
527 2         11 my ($root, $left, $right, $level) = $self->_get_columns;
528              
529 2         7 foreach my $node (@_) {
530 2         39 $self->_attach_node($node, {
531             left_delta => 0,
532             $level => $self->$level,
533             });
534             }
535 2         6964 return $self;
536             }
537             *attach_before = \&attach_left_sibling;
538              
539             # take_cutting
540             # Given a node, cut it from it's current tree and make it the root of a new tree
541             # NOTE2: The root ID must be specified for multi-key primary keys
542             # otherwise it comes from the primary key
543             #
544             sub take_cutting {
545 1     1 1 10385 my $self = shift;
546              
547 1         7 my ($root, $left, $right, $level) = $self->_get_columns;
548              
549             $self->result_source->schema->txn_do(sub {
550 1     1   448 my $p_lft = $self->$left;
551 1         31 my $p_rgt = $self->$right;
552 1 50       16 return $self if $p_lft == $p_rgt + 1;
553              
554 1         4 my $pk = ($self->result_source->primary_columns)[0];
555              
556 1         27 $self->discard_changes;
557 1         3403 my $root_id = $self->$root;
558              
559 1         17 my $p_diff = $p_rgt - $p_lft;
560 1         19 my $l_diff = $self->$level - 1;
561 1         27 my $new_id = $self->$pk;
562             # I'd love to use $self->descendants->update(...),
563             # but it dies with "_strip_cond_qualifiers() is unable to
564             # handle a condition reftype SCALAR".
565             # tough beans.
566 1         27 $self->nodes_rs->search({
567             $root => $root_id,
568             $left => {'>=' => $p_lft },
569             $right => {'<=' => $p_rgt },
570             })->update({
571             $left => \"$left - $p_lft + 1", #"
572             $right => \"$right - $p_lft + 1", #"
573             $root => $new_id,
574             $level => \"$level - $l_diff", #"
575             });
576              
577             # fix up the rest of the tree
578 1         4008 $self->nodes_rs->search({
579             $root => $root_id,
580             $left => { '>=' => $p_rgt},
581             })->update({
582             $left => \"$left - $p_diff", #"
583             $right => \"$right - $p_diff", #"
584             });
585 1         6 });
586 1         2410 return $self;
587             }
588              
589             sub dissolve {
590 1     1 1 4473 my $self = shift;
591 1         6 my ($root, $left, $right, $level) = $self->_get_columns;
592 1         6 my $pk = ($self->result_source->primary_columns)[0];
593 1         28 $self->nodes_rs->search({$root => $self->$root})->update({
594             $level => 1,
595             $left => 1,
596             $right => 2,
597             $root => \"$pk", #"
598             });
599 1         2582 return $self;
600             }
601              
602             # Move a node to the left
603             # Swap position with the sibling on the left
604             # returns the node it exchanged with on success, undef if it is already leftmost sibling
605             #
606             sub move_left {
607 2     2 1 16495 my ($self) = @_;
608              
609 2         9 my $previous = $self->left_sibling;
610 2 100       309 if (! $previous) {
611 1         4 return;
612             }
613 1         11 $previous->attach_left_sibling($self);
614 1         4 return $previous;
615             }
616             *move_previous = \&move_left;
617              
618             # Move a node to the right
619             # Swap position with the sibling on the right
620             # returns the node it exchanged with on success, undef if it is already rightmost sibling
621             #
622             sub move_right {
623 2     2 1 24209 my ($self) = @_;
624              
625 2         11 my $next = $self->right_sibling;
626 2 100       324 if (! $next) {
627 1         7 return;
628             }
629 1         9 $next->attach_right_sibling($self);
630 1         4 return $next;
631             }
632             *move_next = \&move_right;
633              
634             # Move a node to be the leftmost child
635             # Make this node the leftmost sibling
636             # returns the node it exchanged with on success, undef if it is already leftmost sibling
637             sub move_leftmost {
638 2     2 1 23999 my ($self) = @_;
639              
640 2         13 my $first = $self->leftmost_sibling;
641 2 100       368 if (! $first) {
642 1         18 return;
643             }
644 1         10 $first->attach_left_sibling($self);
645 1         5 return $first;
646             }
647             *move_first = \&move_leftmost;
648              
649             # Make this node the rightmost sibling
650             # returns 1 on success, 0 if it is already rightmost sibling
651             sub move_rightmost {
652 2     2 1 26541 my ($self) = @_;
653              
654 2         12 my $last = $self->rightmost_sibling;
655 2 100       310 if (! $last) {
656 1         6 return;
657             }
658 1         6 $last->attach_right_sibling($self);
659 1         5 return $last;
660             }
661             *move_last = \&move_rightmost;
662              
663             # Move this node to the specified position
664             # Returns 1 on success, 0 if it is already in that position
665             #
666       0 0   sub move_to {
667             }
668              
669             # Return a resultset of all siblings excluding the one called on
670             #
671             sub siblings {
672 3     3 1 7752 my ($self) = @_;
673              
674 3         14 my ($root, $left, $right, $level) = $self->_get_columns;
675              
676 3 100       13 if ($self->is_root) {
677             # Root has no siblings
678 1         4 return;
679             }
680 2 100       9 if (wantarray()) {
681 1         6 my @siblings = $self->parent->children({
682             "me.$left" => {'!=', $self->$left },
683             });
684 1         4698 return @siblings;
685             }
686 1         18 my $siblings_rs = $self->parent->children({
687             "me.$left" => {'!=', $self->$left },
688             });
689 1         666 return $siblings_rs;
690             }
691              
692             # Returns a resultset of all siblings to the left of this one
693             #
694             sub left_siblings {
695 11     11 1 8733 my ($self) = @_;
696              
697 11         29 my ($root, $left, $right, $level) = $self->_get_columns;
698              
699 11 50       30 if ($self->is_root) {
700             # Root has no siblings
701 0         0 return;
702             }
703 11 100       37 if (wantarray()) {
704 1         17 my @siblings = $self->parent->children({
705             "me.$left" => {'<', $self->$left },
706             });
707 1         3113 return @siblings;
708             }
709 10         166 my $siblings_rs = $self->parent->children({
710             "me.$left" => {'<', $self->$left },
711             });
712 10         9372 return $siblings_rs;
713             }
714             *previous_siblings = \&left_siblings;
715              
716             # Returns a resultset of all siblings to the right of this one
717             #
718             sub right_siblings {
719 11     11 1 8151 my ($self) = @_;
720              
721 11         31 my ($root, $left, $right, $level) = $self->_get_columns;
722              
723 11 50       31 if ($self->is_root) {
724             # Root has no siblings
725 0         0 return;
726             }
727 11 100       39 if (wantarray()) {
728 1         18 my @siblings = $self->parent->children({
729             "me.$left" => {'>', $self->$left },
730             });
731 1         3133 return @siblings;
732             }
733 10         172 my $siblings_rs = $self->parent->children({
734             "me.$left" => {'>', $self->$left },
735             });
736 10         14457 return $siblings_rs;
737             }
738             *next_siblings = \&right_siblings;
739              
740              
741             # return the sibling to the left of this one
742             #
743             sub left_sibling {
744 4     4 1 4137 my ($self) = @_;
745              
746 4         16 my ($root, $left, $right, $level) = $self->_get_columns;
747              
748 4 50       17 if ($self->is_root) {
749             # Root has no siblings
750 0         0 return;
751             }
752              
753 4         22 my $sibling = $self->left_siblings->search({
754             "me.$right" => $self->$left - 1,
755             },{
756             rows => 1,
757             })->first;
758              
759 4         14981 return $sibling;
760             }
761             *previous_sibling = \&left_sibling;
762              
763             # return the sibling to the right of this one
764             #
765             sub right_sibling {
766 4     4 1 1818 my ($self) = @_;
767              
768 4         17 my ($root, $left, $right, $level) = $self->_get_columns;
769              
770 4 50       17 if ($self->is_root) {
771             # Root has no siblings
772 0         0 return;
773             }
774              
775 4         17 my $sibling = $self->right_siblings->search({
776             "me.$left" => $self->$right + 1,
777             },{
778             rows => 1,
779             })->first;
780              
781 4         16529 return $sibling;
782             }
783             *next_sibling = \&right_sibling;
784              
785             # Returns the leftmost sibling or undef if this is the first sibling
786             #
787             sub leftmost_sibling {
788 4     4 1 1712 my ($self) = @_;
789              
790 4         15 my ($root, $left, $right, $level) = $self->_get_columns;
791              
792 4 50       14 if ($self->is_root) {
793             # Root has no siblings
794 0         0 return;
795             }
796              
797 4         17 my $sibling = $self->left_siblings->search({},{
798             order_by => "me.$left",
799             rows => 1,
800             })->first;
801              
802 4         12997 return $sibling;
803             }
804             *first_sibling = \&leftmost_sibling;
805              
806             # Returns the rightmost sibling or undef if this is the rightmost sibling
807             #
808             sub rightmost_sibling {
809 4     4 1 1706 my ($self) = @_;
810              
811 4         16 my ($root, $left, $right, $level) = $self->_get_columns;
812              
813 4 50       17 if ($self->is_root) {
814             # Root has no siblings
815 0         0 return;
816             }
817              
818 4         20 my $sibling = $self->right_siblings->search({},{
819             order_by => "me.$left desc",
820             rows => 1,
821             })->first;
822              
823 4         12171 return $sibling;
824             }
825             *last_sibling = \&rightmost_sibling;
826              
827             # Insert a sibling to the right of this one
828             #
829             sub create_right_sibling {
830 3     3 1 104 my ($self, $args) = @_;
831              
832 3         10 my ($root, $left, $right, $level) = $self->_get_columns;
833              
834 3         52 return $self->_insert_node({
835             $left => $self->$right + 1,
836             $right => $self->$right + 2,
837             $level => $self->$level,
838             other_args => $args,
839             });
840             }
841              
842             # Insert a sibling to the left of this one
843             #
844             sub create_left_sibling {
845 0     0 1 0 my ($self, $args) = @_;
846              
847 0         0 my ($root, $left, $right, $level) = $self->_get_columns;
848              
849 0         0 return $self->_insert_node({
850             $left => $self->$left,
851             $right => $self->$left + 1,
852             $level => $self->$level,
853             other_args => $args,
854             });
855             }
856              
857             # Insert a rightmost child
858             #
859             sub create_rightmost_child {
860 1     1 1 4404 my ($self, $args) = @_;
861              
862 1         6 my ($root, $left, $right, $level) = $self->_get_columns;
863              
864 1         19 return $self->_insert_node({
865             $left => $self->$right,
866             $right => $self->$right + 1,
867             $level => $self->$level + 1,
868             other_args => $args,
869             });
870             }
871              
872             # Insert a leftmost child
873             #
874             sub create_leftmost_child {
875 0     0 1 0 my ($self, $args) = @_;
876              
877 0         0 my ($root, $left, $right, $level) = $self->_get_columns;
878              
879 0         0 return $self->_insert_node({
880             $left => $self->$left + 1,
881             $right => $self->$left + 2,
882             $level => $self->$level + 1,
883             other_args => $args,
884             });
885             }
886              
887             # Given a primary key, determine if it is a descendant of
888             # this object
889             #
890             sub has_descendant {
891 0     0 0 0 my ($self) = shift;
892              
893 0         0 my $descendant = $self->result_source->resultset->find(@_);
894 0 0       0 if (! $descendant) {
895 0         0 return;
896             }
897              
898 0         0 my ($root, $left, $right, $level) = $self->_get_columns;
899              
900 0 0 0     0 if ($descendant->$left > $self->$left && $descendant->$right < $self->$right) {
901 0         0 return 1;
902             }
903 0         0 return;
904             }
905              
906             # Given a primary key, determine if it is an ancestor of
907             # this object
908             #
909             sub has_ancestor {
910 0     0 0 0 my ($self) = shift;
911              
912 0         0 my $ancestor = $self->result_source->resultset->find(@_);
913 0 0       0 if (! $ancestor) {
914 0         0 return;
915             }
916              
917 0         0 my ($root, $left, $right, $level) = $self->_get_columns;
918              
919 0 0 0     0 if ($self->$left > $ancestor->$left && $self->$right < $ancestor->$right) {
920 0         0 return 1;
921             }
922 0         0 return;
923             }
924              
925             # returns true if this node is a root node
926             #
927             sub is_root {
928 87     87 1 75724 my ($self) = @_;
929              
930 87 100       277 if ($self->get_column( $self->tree_columns->{level_column} ) == 0) {
931 9         453 return 1;
932             }
933 78         2758 return;
934             }
935              
936             # returns true if this node is a leaf node (no children)
937             #
938             sub is_leaf {
939 8     8 1 19 my ($self) = @_;
940              
941 8 100       23 if ($self->get_column( $self->tree_columns->{right_column}) - $self->get_column( $self->tree_columns->{left_column}) == 1) {
942 6         176 return 1;
943             }
944 2         57 return;
945             }
946              
947             # returns true if this node is a branch (has children)
948             #
949             sub is_branch {
950 4     4 1 11 my ($self) = @_;
951              
952 4         12 return !$self->is_leaf;
953             }
954              
955             1;
956              
957             =head1 NAME
958              
959             DBIx::Class::Tree::NestedSet - Manage trees of data using the nested set model
960              
961             =head1 SYNOPSIS
962              
963             Create a table for your tree data.
964              
965             CREATE TABLE Department (
966             id INTEGER PRIMARY KEY AUTOINCREMENT,
967             root_id integer,
968             lft integer NOT NULL,
969             rgt integer NOT NULL,
970             level integer NOT NULL,
971             name text NOT NULL,
972             );
973              
974             In your Schema or DB class add Tree::NestedSet to the top
975             of the component list.
976              
977             __PACKAGE__->load_components(qw( Tree::NestedSet ... ));
978              
979             Specify the columns required by the module.
980              
981             package My::Department;
982             __PACKAGE__->tree_columns({
983             root_column => 'root_id',
984             left_column => 'lft',
985             right_column => 'rgt',
986             level_column => 'level',
987             });
988              
989             Using it:
990              
991             my $root = My::Department->create({ ... });
992             my $child = $root->add_to_children({ ... });
993              
994             my $rs = $root->children;
995             my @descendants = $root->children;
996              
997             my $parent = $child->parent;
998             my $rs = $child->ancestors;
999             my @ancestors = $child->ancestors;
1000              
1001             =head1 DESCRIPTION
1002              
1003             This module provides methods for working with nested set trees. The nested tree
1004             model is a way of representing hierarchical information in a database. This
1005             takes a different approach to the Adjacency List implementation. (see
1006             L which uses C relationships in a recursive manner.
1007              
1008             The NestedSet implementation can be more efficient for most searches than the Adjacency List Implementation,
1009             for example, to obtain all descendants requires recursive queries in the Adjacency List
1010             implementation but is a single query in the NestedSet implementation.
1011              
1012             The trade-off is that NestedSet inserts are more expensive so it is most useful if
1013             you have an application that does many reads but few inserts.
1014              
1015             More about NestedSets can be found at L
1016              
1017             Oh, and although I give some code examples of familial relationships (where there are usually
1018             two parents), both Adjacency List and NestedSet implementations can only have one parent.
1019              
1020             =head1 RELATIONS
1021              
1022             This module automatically creates several relationships.
1023              
1024             =head2 root
1025              
1026             $root_node = $node->root;
1027              
1028             A belongs_to relation to the root of C<$node>s tree.
1029              
1030             =head2 nodes
1031              
1032             $all_nodes = $node->nodes;
1033             $new_node = $node->add_to_nodes({name => 'Mens Wear'});
1034              
1035             A has_many relationship to all the nodes of C<$node>s tree.
1036              
1037             Adding to this relationship creates a rightmost child to C<$node>.
1038              
1039             =head2 parent
1040              
1041             $parent = $node->parent;
1042              
1043             A belongs_to relationship to the parent node of C<$node>s tree.
1044              
1045             Note that only the root node does not have a parent.
1046              
1047             =head2 children
1048              
1049             $rs = $node->children;
1050             @children = $node->children;
1051             $child = $node->add_to_children({name => 'Toys'});
1052              
1053             A has_many relation to the children of C<$node>.
1054              
1055             Adding to this relationship creates a rightmost child to C<$node>.
1056              
1057             =head2 descendants
1058              
1059             $rs = $node->descendants;
1060             @descendants = $node->descendants;
1061             $child = $node->add_to_descendants({name => 'Mens Wear'});
1062              
1063             A has_many relation to the descendants of C<$node>.
1064              
1065             Adding to this relationship creates a rightmost child to C<$node>.
1066              
1067             =head2 ancestors
1068              
1069             $rs = $node->ancestors;
1070             @ancestors = $node->ancestors;
1071             $parent = $node->add_to_ancestors({name => 'Head office'});
1072              
1073             A has_many relation to the ancestors of C<$node>.
1074              
1075             Adding to this relationship creates a new node in place of C<$node>
1076             and makes it the parent of C<$node>. All descendants of C<$node>
1077             will likewise be pushed town the hierarchy.
1078              
1079             =head1 METHODS
1080              
1081             Many methods have alternative names, e.g. C and C
1082              
1083             This is in deference to the L module which uses terms
1084             C C C and C.
1085              
1086             Similarly L uses terms C, C,
1087             C and C
1088              
1089             However, my preference to use terms C and C consistently when using
1090             this module. However, the other names are available if you are more familiar with
1091             those modules.
1092              
1093             =head2 tree_columns
1094              
1095             __PACKAGE__->tree_columns({
1096             left_column => 'lft',
1097             right_column => 'rgt',
1098             root_column => 'root_id',
1099             level_column => 'level',
1100             });
1101              
1102             Declare the name of the columns defined in the database schema.
1103              
1104             None of these columns should be modified outside if this module. left_column
1105             and right_column are unlikely to be of any use to your application. They
1106             should be integer fields.
1107              
1108             Multiple trees are allowed in the same table, each tree will have a unique
1109             value in the root_column. In the current implementation this should be an
1110             integer field
1111              
1112             The level_column may be of use in your application, it defines the depth of
1113             each node in the tree (with the root at level zero).
1114              
1115             =head2 create
1116              
1117             my $tree = $schema->resultset('My::Department')->create({
1118             name = 'Head Office',
1119             });
1120              
1121             my $tree = $schema->resultset('My::Department')->create({
1122             name = 'UK Office',
1123             root_id = $uk_office_ident,
1124             });
1125              
1126             Creates a new root node.
1127              
1128             If the root_column (root_id) is not provided then it defaults to producing
1129             a node where the root_column has the same value as the primary key. This will
1130             croak if the table is defined with multiple key primary index.
1131              
1132             Note that no checks (yet) are made to stop you creating another key with
1133             the same root_id as an existing tree. If you do so you will get into a terrible
1134             mess!
1135              
1136             =head2 delete
1137              
1138             $department->delete;
1139              
1140             This will delete the node and all descendants. Cascade Delete is turned off
1141             in the has_many relationships C C C so that
1142             delete DTRT.
1143              
1144             =head2 is_root
1145              
1146             if ($node->is_root) {
1147             print "Node is a root\n";
1148             }
1149              
1150             Returns true if the C<$node> is a root node
1151              
1152             =head2 is_branch
1153              
1154             $has_children = $node->is_branch;
1155              
1156             Returns true if the node is a branche (i.e. has children)
1157              
1158             =head2 is_leaf
1159              
1160             $is_terminal_node = $node->is_leaf;
1161              
1162             Returns true if the node is a leaf (i.e. it has no children)
1163              
1164             =head2 siblings
1165              
1166             @siblings = $node->siblings;
1167             $siblings_rs = $node->siblings;
1168              
1169             Returns all siblings of this C<$node> excluding C<$node> itself.
1170              
1171             Since a root node has no siblings it returns undef.
1172              
1173             =head2 left_siblings (or previous_siblings)
1174              
1175             @younger_siblings = $node->left_siblings;
1176             $younger_siblings_rs = $node->left_siblings;
1177              
1178             Returns all siblings of this C<$node> to the left this C<$node>.
1179              
1180             Since a root node has no siblings it returns undef.
1181              
1182             =head2 right_siblings (or next_siblings)
1183              
1184             @older_siblings = $node->right_siblings;
1185             $older_siblings_rs = $node->right_siblings;
1186              
1187             Returns all siblings of this C<$node> to the right of this C<$node>.
1188              
1189             Since a root node has no siblings it returns undef.
1190              
1191             =head2 left_sibling (or previous_sibling)
1192              
1193             $younger_sibling = $node->left_sibling;
1194              
1195             Returns the sibling immediately to the left of this C<$node> (if any).
1196              
1197             =head2 right_sibling (or next_sibling)
1198              
1199             $older_sibling = $node->right_sibling;
1200              
1201             Returns the sibling immediately to the right of this C<$node> (if any).
1202              
1203             =head2 leftmost_sibling (or first_sibling)
1204              
1205             $youngest_sibling = $node->leftmost_sibling;
1206              
1207             Returns the left most sibling relative to this C<$node> (if any).
1208              
1209             Does not return this C<$node> if this node is the leftmost sibling.
1210              
1211             =head2 rightmost_sibling (or last_sibling)
1212              
1213             $oldest_sibling = $node->rightmost_sibling;
1214              
1215             Returns the right most sibling relative to this C<$node> (if any).
1216              
1217             Does not return this C<$node> if this node is the rightmost sibling.
1218              
1219             =head2 CREATE METHODS
1220              
1221             The following create methods create a new node in relation to an
1222             existing node.
1223              
1224             =head2 create_right_sibling
1225              
1226             $bart->create_right_sibling({ name => 'Lisa' });
1227              
1228             Create a new node as a right sibling to C<$bart>.
1229              
1230             =head2 create_left_sibling
1231              
1232             $bart->create_left_sibling({ name => 'Maggie' });
1233              
1234             Create a new node as a left sibling to C<$bart>.
1235              
1236             =head2 create_rightmost_child
1237              
1238             $homer->create_rightmost_child({ name => 'Lisa' });
1239              
1240             Create a new node as a rightmost child to C<$homer>
1241              
1242             =head2 create_leftmost_child
1243              
1244             $homer->create_leftmost_child({ name => 'Maggie' });
1245              
1246             Create a new node as a leftmost child to C<$homer>
1247              
1248              
1249             =head2 ATTACH METHODS
1250              
1251             The following attach methods take an existing node (and all of it's
1252             descendants) and attaches them to the tree in relation to an existing node.
1253              
1254             The node being inserted can either be from the same tree (as identified
1255             by the root_column) or from another tree. If the root of another tree is
1256             attached then the whole of that tree becomes a sub-tree of this node's
1257             tree.
1258              
1259             The only restriction is that the node being attached cannot be an ancestor
1260             of this node.
1261              
1262             When attaching multiple nodes we try to DWIM so that the order they are specified
1263             in the call represents the order they appear in the siblings list.
1264              
1265             e.g. if we had a parent with children A,B,C,D,E
1266              
1267             and we attached nodes 1,2,3 in the following calls, we expect the following results.
1268              
1269             $parent->attach_rightmost_child 1,2,3 gives us children A,B,C,D,E,1,2,3
1270              
1271             $parent->attach_leftmost_child 1,2,3 gives us children 1,2,3,A,B,C,D,E
1272              
1273             $child_C->attach_right_sibling 1,2,3 gives us children A,B,C,1,2,3,D,E
1274              
1275             $child_C->attach_left_sibling 1,2,3 gives us children A,B,1,2,3,C,D,E
1276              
1277             $child_C->attach_rightmost_sibling 1,2,3 gives us children A,B,C,D,E,1,2,3
1278              
1279             $child_C->attach_leftmost_sibling 1,2,3 gives us children 1,2,3,A,B,C,D,E
1280              
1281             =head2 attach_rightmost_child (or append_child)
1282              
1283             $parent->attach_rightmost_child($other_node);
1284             $parent->attach_rightmost_child($other_node_1, $other_node_2, ...);
1285              
1286             Attaches the other_nodes to C<$parent> as the rightmost children.
1287              
1288             =head2 attach_leftmost_child
1289              
1290             $parent->attach_leftmost_child($other_node);
1291             $parent->attach_leftmost_child($other_node_1, $other_node_2, ...);
1292              
1293             Attaches the other_nodes to C<$parent> as the leftmost children.
1294              
1295             =head2 attach_right_sibling (or attach_after)
1296              
1297             $node->attach_right_sibling($other_node);
1298             $node->attach_right_sibling($other_node_1, $other_node_2, ...);
1299              
1300             Attaches the other_nodes to C<$node> as it's siblings.
1301              
1302             =head2 attach_left_sibling
1303              
1304             $node->attach_left_sibling($other_node);
1305             $node->attach_left_sibling($other_node_1, $other_node_2, ...);
1306              
1307             Attaches the other_nodes to C<$node> as it's left siblings.
1308              
1309             =head2 attach_rightmost_sibling
1310              
1311             $node->attach_rightmost_sibling($other_node);
1312             $node->attach_rightmost_sibling($other_node_1, $other_node_2, ...);
1313              
1314             Attaches the other_nodes to C<$node> as it's rightmost siblings.
1315              
1316             =head2 attach_leftmost_sibling
1317              
1318             $node->attach_leftmost_sibling($other_node);
1319             $node->attach_leftmost_sibling($other_node_1, $other_node_2, ...);
1320              
1321             Attaches the other_nodes to C<$node> as it's leftmost siblings.
1322              
1323             =head2 move_left (or move_previous)
1324              
1325             $node->move_left;
1326              
1327             Exchange the C<$node> with the sibling immediately to the left and return the
1328             node it exchanged with.
1329              
1330             If the C<$node> is already the leftmost node then no exchange takes place
1331             and the method returns undef.
1332              
1333             =head2 move_right (or move_next)
1334              
1335             $node->move_right;
1336              
1337             Exchange the C<$node> with the sibling immediately to the right and return the
1338             node it exchanged with.
1339              
1340             If the C<$node> is already the rightmost node then no exchange takes place
1341             and the method returns undef.
1342              
1343             =head2 move_leftmost (or move_first)
1344              
1345             $node->move_leftmost;
1346              
1347             Exchange the C<$node> with the leftmost sibling and return the
1348             node it exchanged with.
1349              
1350             If the C<$node> is already the leftmost node then no exchange takes place
1351             and the method returns undef.
1352              
1353             =head2 move_rightmost (or move_last)
1354              
1355             $node->move_rightmost;
1356              
1357             Exchange the C<$node> with the rightmost sibling and return the
1358             node it exchanged with.
1359              
1360             If the C<$node> is already the rightmost node then no exchange takes place
1361             and the method returns undef.
1362              
1363             =head2 CUTTING METHODS
1364              
1365             =head2 take_cutting
1366              
1367             Cuts the invocant and its descendants out of the tree they are in,
1368             making the invocant the root of a new tree. Returns the modified
1369             invocant.
1370              
1371             =head2 dissolve
1372              
1373             Dissolves the entire thread, that is turn each node of the thread into a
1374             single-item tree of its own.
1375              
1376             =head1 CAVEATS
1377              
1378             =head2 Multiple Column Primary Keys
1379              
1380             Support for Multiple Column Primary Keys is limited (mainly because I rarely
1381             use them) but I have tried to make it possible to use them. Please let me
1382             know if this does not work as well as you expect.
1383              
1384             =head2 discard_changes
1385              
1386             By the nature of Nested Set implementations, moving, inserting or deleting
1387             nodes in the tree will potentially update many (sometimes most) other nodes.
1388              
1389             Even if you have preloaded some of the objects, if you make a change to one
1390             object the other objects will not reflect their new value until you have
1391             reloaded them from the database.
1392             (see L)
1393              
1394             A simple demonstration of this
1395              
1396             $grampa = $schema->schema->resultset('Simpsons')->create({ name => 'Abraham' });
1397             $homer = $grampa->add_children({name => 'Homer'});
1398             $bart = $homer->add_children({name => 'Bart'});
1399              
1400             The methods in this module will do their best to keep instances that they know
1401             about updated. For example the first call to C in the above example
1402             will update C<$grampa> and C<$homer> with the latest changes to the database.
1403              
1404             However, the second call to C only knows about C<$homer> and C<$bart>
1405             and in adding a new node to the tree it will update the C<$grampa> node in
1406             the database. To ensure you have the latest changes do the following.
1407              
1408             $grampa->discard_changes.
1409              
1410             Not doing so will have unpredictable results.
1411              
1412             =head1 AUTHORS
1413              
1414             Code by Ian Docherty Epause@iandocherty.comE
1415              
1416             Based on original code by Florian Ragwitz Erafl@debian.orgE
1417              
1418             Incorporating ideas and code from Pedro Melo Emelo@simplicidade.orgE
1419              
1420             Special thanks to Moritz Lenz who sent in lots of patches and changes for version 0.08
1421              
1422             =head1 COPYRIGHT AND LICENSE
1423              
1424             Copyright (c) 2009-2011 The above authors
1425              
1426             This is free software; you can redistribute it and/or modify
1427             it under the same terms as Perl itself, either Perl version 5.10.0 or,
1428             at your option, any later version of Perl 5 you may have available.
1429              
1430             =cut