File Coverage

lib/MojoX/Tree.pm
Criterion Covered Total %
statement 18 127 14.1
branch 0 38 0.0
condition 0 30 0.0
subroutine 6 15 40.0
pod 5 8 62.5
total 29 218 13.3


line stmt bran cond sub pod time code
1             package MojoX::Tree;
2 1     1   406240 use Mojo::Base -base;
  1         3  
  1         9  
3 1     1   213 use Mojo::Util qw(dumper);
  1         3  
  1         65  
4 1     1   6 use Mojo::Collection 'c';
  1         2  
  1         62  
5 1     1   1440 use DBI;
  1         12892  
  1         58  
6 1     1   8 use Carp qw(croak);
  1         1  
  1         730  
7            
8             our $VERSION = '0.01';
9              
10             sub new {
11 0     0 1   my $class = shift;
12 0           my %args = @_;
13              
14 0           my $config = {};
15 0 0 0       if(exists $args{'mysql'} && $args{'mysql'} && ref $args{'mysql'} eq 'MojoX::Mysql'){
      0        
16 0           $config->{'mysql'} = $args{'mysql'};
17             }
18             else{
19 0           croak qq/invalid MojoX::Mysql object/;
20             }
21              
22 0 0 0       if(exists $args{'table'} && $args{'table'} && $args{'table'} =~ m/^[0-9a-z_-]+$/){
      0        
23 0           $config->{'table'} = $args{'table'};
24             }
25             else{
26 0           croak qq/invalid table/;
27             }
28              
29 0 0 0       if(exists $args{'column'} && $args{'column'} && ref $args{'column'} eq 'HASH'){
      0        
30 0           $config->{'column'} = $args{'column'};
31             }
32             else{
33 0           croak qq/invalid column/;
34             }
35              
36 0 0 0       if(exists $args{'length'} && $args{'length'} && $args{'length'} =~ m/^[0-9]+$/){
      0        
37 0           $config->{'length'} = $args{'length'};
38             }
39             else{
40 0           croak qq/invalid column/;
41             }
42              
43              
44 0           return $class->SUPER::new($config);
45             }
46              
47             sub mysql {
48 0     0 0   return shift->{'mysql'};
49             }
50              
51             sub add {
52 0     0 1   my ($self,$name,$parent_id) = @_;
53              
54 0           my $table = $self->{'table'};
55 0           my $column_id = $self->{'column'}->{'id'};
56 0           my $column_name = $self->{'column'}->{'name'};
57 0           my $column_path = $self->{'column'}->{'path'};
58 0           my $column_level = $self->{'column'}->{'level'};
59 0           my $column_parent_id = $self->{'column'}->{'parent_id'};
60              
61 0           my $parent_path = undef;
62 0 0 0       if(defined $parent_id && $parent_id){
63 0           my $get_id = $self->get_id($parent_id);
64 0           $parent_path = $get_id->{'path'};
65             }
66              
67 0 0         croak "invalid name" if(!$name);
68              
69             # Создаем запись
70 0           my ($insertid,$counter) = $self->mysql->do("INSERT INTO `$table` (`$column_name`) VALUES (?)", $name);
71              
72             # Формируем материлизованный путь
73 0           my $path = $self->make_path($insertid);
74              
75 0 0         $path = $parent_path.$path if(defined $parent_path);
76 0           my $level = $self->make_level($path); # Узнает текущий уровень
77              
78 0           my (undef,$update_counter) = $self->mysql->do(
79             "UPDATE `$table` SET `$column_path` = ?, `$column_level` = ?, `$column_parent_id` = ? WHERE `$column_id` = ?;",
80             $path,$level,$parent_id,$insertid
81             );
82              
83 0 0         croak "invalid update table" if($update_counter != 1);
84 0           return $insertid;
85             }
86              
87             # Удаляет текущего элемент и детей
88             sub delete {
89 0     0 1   my ($self,$id) = @_;
90              
91 0           my $path = undef;
92 0           my $get_id = $self->get_id($id);
93 0 0         if(defined $get_id){
94 0           $path = $get_id->{'path'};
95             }
96             else{
97 0           croak "invalid id:$id";
98             }
99              
100 0           my $table = $self->{'table'};
101 0           my $column_path = $self->{'column'}->{'path'};
102 0           my ($insertid,$counter) = $self->mysql->do("DELETE FROM `$table` WHERE `$column_path` LIKE '$path%';");
103 0 0         if($counter > 0){
104 0           return $counter;
105             }
106             else{
107 0           croak "Unable to delete";
108             }
109             }
110              
111             sub move {
112 0     0 1   my ($self,$id,$target_id) = @_;
113 0           my $table = $self->{'table'};
114 0           my $column_id = $self->{'column'}->{'id'};
115 0           my $column_name = $self->{'column'}->{'name'};
116 0           my $column_path = $self->{'column'}->{'path'};
117 0           my $column_level = $self->{'column'}->{'level'};
118 0           my $column_parent_id = $self->{'column'}->{'parent_id'};
119              
120 0           my $get_id = $self->get_id($id);
121 0 0         croak "invalid id:$id" if(!defined $id);
122              
123 0           my $get_target_id = $self->get_id($target_id);
124 0 0         croak "invalid id:$get_target_id" if(!defined $get_target_id);
125              
126 0 0         croak "Impossible to transfer to itself or children" if($id eq $target_id);
127              
128 0           my $path = $get_id->{'path'};
129 0           my $path_target = $get_target_id->{'path'};
130 0 0         croak "Impossible to transfer to itself or children" if($path =~ m/^$path_target/);
131              
132 0           my $length = $self->{'length'};
133 0           my $collection = $self->mysql->query("SELECT `$column_id` as `id`, `$column_path` as `path` FROM `$table` WHERE `$column_path` LIKE '$path%';");
134             $collection->each(sub {
135 0     0     my $e = shift;
136 0           my $id = $e->{'id'};
137 0 0         if($e->{'path'} =~ m/(?($path\d*))/g){
138 1     1   523 my $path = $path_target.$+{'path'};
  1         331  
  1         456  
  0            
139 0           my $level = $self->make_level($path);
140              
141 0           my $parent_id = 'NULL';
142 0 0         $parent_id = int $+{'parent_id'} if($path =~ m/(?(\d{$length}))\d{$length}$/);
143 0           $self->mysql->do("UPDATE `$table` SET `$column_path` = ?, `level` = ?, `$column_parent_id` = ? WHERE `$column_id` = ?",$path,$level,$parent_id,$id);
144             }
145 0           });
146             }
147              
148             # Получение очереди по id
149             sub get_id {
150 0     0 1   my ($self,$id) = @_;
151              
152 0           my $table = $self->{'table'};
153 0           my $column_id = $self->{'column'}->{'id'};
154 0           my $column_name = $self->{'column'}->{'name'};
155 0           my $column_path = $self->{'column'}->{'path'};
156 0           my $column_level = $self->{'column'}->{'level'};
157 0           my $column_parent_id = $self->{'column'}->{'parent_id'};
158              
159 0           my ($collection,$counter) = $self->mysql->query("SELECT `$column_id`, `$column_path`, `$column_name`, `$column_level`, `$column_parent_id` FROM `$table` WHERE `$column_id` = ? LIMIT 1", $id);
160 0 0         croak "invalid id:$id" if($counter eq '0E0');
161              
162 0           my $result = $collection->last;
163              
164             # Получаем всех детей
165 0           my $path = $result->{'path'};
166 0           my $level = $result->{'level'};
167 0           $result->{'children'} = $self->mysql->query("
168             SELECT `$column_id`, `$column_path`, `$column_name`, `$column_level`, `$column_parent_id` FROM `$table`
169             WHERE `$column_path` LIKE '$path%' AND `level` != ?
170             ",$level);
171              
172             # Получаем всех родителей
173 0           my @parent = ();
174 0           my $length = $self->{'length'};
175 0           for(($path =~ m/(\d{$length})/g)){
176 0           push(@parent, int $_);
177             }
178 0           @parent = grep(!/$id/, @parent);
179 0           my $parent = join(",",@parent);
180              
181 0 0 0       if(defined $parent && $parent){
182 0           $parent = $self->mysql->query("SELECT `$column_id`, `$column_path`, `$column_name`, `$column_level`, `$column_parent_id` FROM `$table` WHERE `$column_id` IN($parent)");
183             }
184             else {
185 0           $parent = c();
186             }
187              
188 0           $result->{'parent'} = $parent;
189 0           return $result;
190             }
191              
192             sub make_path {
193 0     0 0   my ($self,$id) = @_;
194 0           my $length = $self->{'length'};
195 0           my $length_id = length $id;
196 0 0         if($length_id < $length){
197 0           my $zero = '0' x ($length - $length_id);
198 0           $id = $zero.$id;
199             }
200 0           return $id;
201             }
202              
203             sub make_level {
204 0     0 0   my ($self,$path) = @_;
205 0           my $length = $self->{'length'};
206 0           my @counter = ($path =~ m/([0-9]{$length})/g);
207 0           return scalar @counter;
208             }
209              
210              
211             1;
212              
213             __END__