File Coverage

blib/lib/DBI/Easy/Record/Collection.pm
Criterion Covered Total %
statement 108 180 60.0
branch 24 52 46.1
condition 28 62 45.1
subroutine 12 18 66.6
pod 0 15 0.0
total 172 327 52.6


line stmt bran cond sub pod time code
1             package DBI::Easy::Record::Collection;
2              
3 6     6   6036 use Class::Easy;
  6         121  
  6         43  
4              
5 6     6   896 use base qw(DBI::Easy);
  6         12  
  6         17940  
6              
7             our $wrapper = 1;
8              
9             has 'filter', is => 'rw', default => {};
10             has 'join_table', is => 'rw';
11              
12             sub new_record {
13 4     4 0 2466 my $self = shift;
14 4   50     145 my $params = shift || {};
15            
16 4         157 my $rec_pack = $self->record_package;
17            
18 4 50       34 my $rec = $rec_pack->new ({%$params, %{$self->filter || {}}});
  4         21  
19             }
20              
21             sub natural_join {
22 1     1 0 3 my $self = shift;
23            
24 1         3 my $join = join ' ', map {'natural join ' . $_->table_quoted} @_;
  1         8  
25 1         13 $self->join_table ($join);
26             }
27              
28             sub make_sql_and_bind {
29 39     39 0 67 my $self = shift;
30 39         64 my $method = shift;
31            
32 39         49 my $set;
33 39         70 my $where = {};
34 39         73 my $suffix = '';
35 39         60 my $bind_suffix;
36            
37             my %args;
38            
39             # legacy syntax
40 39 100 66     256 if (! defined $_[0] or ref $_[0]) {
41 37         59 $set = shift;
42 37   100     144 $where = shift || {};
43 37   50     177 $suffix = shift || '';
44              
45 37         96 $bind_suffix = shift;
46 37         83 %args = (@_);
47             } else {
48 2         7 %args = (@_);
49 2   50     10 $where = delete $args{where} || {};
50 2         5 $set = delete $args{set};
51 2   50     12 $suffix = delete $args{suffix} || '';
52 2         6 $bind_suffix = delete $args{bind};
53             }
54              
55             # if we call collection method from package name, we must create collection
56             # object automatically
57 39 100       340 $self = $self->new
58             unless ref $self;
59            
60 39         775 my $filter = $self->filter;
61            
62 39         903 my %params = (
63             where => [
64             $self->fields_to_columns ($filter),
65             $self->fields_to_columns ($where)
66             ],
67             suffix => $suffix,
68             %args
69             );
70            
71 39 50       321 if ($method eq 'sql_update') {
72 0         0 $params{set} = $self->fields_to_columns ($set);
73             }
74            
75             # use Data::Dumper;
76             # warn "$method => " . Dumper \%params;
77            
78 39         267 my ($select, $bind) = $self->$method (%params);
79            
80 39 100       119 push @$bind, @{$bind_suffix || []};
  39         187  
81            
82 39 50       521 debug 'sql: \'', $select, '\' => ', defined $bind ? join ', ', @$bind : '[empty]';
83            
84 39         4952 return ($select, $bind);
85            
86             }
87              
88             # legacy
89             sub list {
90 16     16 0 5553 my $self = shift;
91 16   100     70 my $where = shift || {};
92 16   50     93 my $suffix = shift || '';
93 16   100     73 my $bind_suffix = shift || [];
94 16         44 my %params = @_;
95            
96 16         172 return $self->records (where => $where, suffix => [$suffix, @$bind_suffix], %params);
97             }
98              
99             sub records {
100 22     22 0 2381 my $self = shift;
101 22         46 my $where;
102             my %params;
103              
104 22 100       65 if (ref $_[0]) {
105 1         2 $where = shift;
106 1         4 %params = @_;
107             } else {
108 21         87 %params = @_;
109 21   100     318 $where = delete $params{where} || {};
110             }
111            
112 22         41 my $suffix = '';
113 22         46 my $bind_suffix = [];
114            
115             #TODO: REGRESSION FIX !!!
116            
117 22 100 66     242 if ($params{suffix} and ref $params{suffix} and ref $params{suffix} eq 'ARRAY') {
    50 66        
118 16   50     27 $suffix = shift @{$params{suffix}} || '';
119 16         40 $bind_suffix = delete $params{suffix};
120             } elsif ($params{suffix}) {
121 0   0     0 $suffix = delete $params{suffix} || '';
122             }
123            
124 22         111 my @fetch_params = $self->make_sql_and_bind ('sql_select', undef, $where, $suffix, $bind_suffix, %params);
125            
126 22 50 33     190 if ($params{fetch_handler} and ref $params{fetch_handler} eq 'CODE') {
127            
128 0         0 debug "fetch by record";
129            
130             $self->fetch_handled (@fetch_params, sub {
131 0     0   0 my $row = shift;
132            
133 0         0 my $rec = $self->record_package->new (column_values => $row);
134            
135 0         0 return $params{fetch_handler}->($rec);
136 0         0 });
137            
138            
139             } else {
140 22         193 my $db_result = $self->fetch_arrayref (@fetch_params);
141            
142 22         114 debug "result count: ", $#$db_result+1;
143            
144 22         10937 $self->columns_to_fields_in_place ($db_result);
145            
146 22         387 return $db_result;
147             }
148             }
149              
150             sub list_of_record_hashes {
151 1     1 0 76 my $self = shift;
152 1         5 my $records = $self->records (@_);
153            
154 1         5 my $list_of_hashes = [map {$_->hash} @$records];
  1         20  
155            
156 1         8 return $list_of_hashes;
157             }
158              
159             sub update {
160 0     0 0 0 my $self = shift;
161            
162 0         0 my ($sql, $bind) = $self->make_sql_and_bind ('sql_update', @_);
163            
164 0         0 my $db_result = $self->no_fetch ($sql, $bind);
165            
166 0         0 debug "rows affected: ", $db_result;
167            
168 0         0 return $db_result;
169             }
170              
171              
172             sub count {
173 16     16 0 4274 my $self = shift;
174            
175 16         31 my ($select, $bind);
176            
177 16 100 100     122 if (ref $_[0] or @_ % 2) { # make_sql_and_bind (set, where, suffix, bind)
178 7         32 ($select, $bind) = $self->make_sql_and_bind ('sql_select_count', undef, @_);
179            
180             } else { # make_sql_and_bind (set => set, where => where, ...)
181 9         61 ($select, $bind) = $self->make_sql_and_bind ('sql_select_count', @_);
182             }
183            
184 16         113 my $db_result = $self->fetch_single ($select, $bind);
185            
186 16         61 debug "result count: ", $db_result;
187            
188 16         2279 return $db_result;
189            
190             }
191              
192             sub delete {
193 1     1 0 36 my $self = shift;
194            
195 1         3 my ($select, $bind);
196            
197 1 50 33     17 if (ref $_[0] or @_ % 2) { # make_sql_and_bind (set, where, suffix, bind)
198 0         0 ($select, $bind) = $self->make_sql_and_bind ('sql_delete', undef, @_);
199            
200             } else { # make_sql_and_bind (set => set, where => where, ...)
201 1         14 ($select, $bind) = $self->make_sql_and_bind ('sql_delete', @_);
202             }
203            
204 1         14 my $db_result = $self->no_fetch ($select, $bind);
205            
206 1         11 debug "rows affected: ", $db_result;
207            
208 1         353 return $db_result;
209             }
210              
211             sub tree {
212 0     0 0 0 my $self = shift;
213 0         0 my $keys = shift;
214 0         0 my $where = shift;
215 0         0 my $suffix = shift;
216            
217 0         0 my $ref = ref $self;
218              
219 0         0 my $where_w_filter = $where;
220 0         0 my $filter = $self->filter;
221 0 0 0     0 $where_w_filter = {%$where, %$filter}
222             if defined $filter and ref $filter eq 'HASH';
223            
224 0         0 my $where_prefixed = $self->fields_to_columns ($where_w_filter);
225            
226 0         0 my ($select, $bind) = $self->sql_select (where => $where_prefixed, suffix => $suffix);
227            
228             # warn $select, ' => ', defined $bind ? join ', ', @$bind : '[empty]';
229            
230 0         0 my $db_result = $self->fetch_hashref ($select, $keys, $bind);
231             #my $db_result = $self->fetch_arrayref ($select, $bind);
232            
233 0         0 $self->columns_to_fields_in_place ($db_result, $keys);
234            
235 0         0 return $db_result;
236            
237             }
238              
239             sub item {
240 0     0 0 0 my $self = shift;
241 0         0 my $where = shift;
242 0   0     0 my $suffix = shift || '';
243            
244 0         0 my $result = $self->list ($where, $suffix . ' limit 1');
245            
246             # programmer must be warned about multiple values
247 0         0 return $result->[0];
248             }
249              
250             sub new_record_from_request {
251 0     0 0 0 my $self = shift;
252 0         0 my $request = shift;
253              
254 0         0 my $rec_pack = $self->record_package;
255            
256 0         0 my $rec = $rec_pack->new ({%{$self->filter}});
  0         0  
257 0         0 $rec->apply_request_params ($request);
258            
259 0         0 return $rec;
260             }
261              
262             sub columns_to_fields_in_place {
263 22     22 0 41 my $self = shift;
264 22         408 my $rows = shift;
265            
266 22         4061 my $rec_pack = $self->record_package;
267            
268 22 50       221 if (UNIVERSAL::isa ($rows, 'ARRAY')) {
    0          
269            
270 22         83 foreach my $row_counter (0 .. $#$rows) {
271            
272 39         67 my $row = $rows->[$row_counter];
273            
274 39         922 $rows->[$row_counter] = $rec_pack->new (column_values => $row);
275             }
276             } elsif (UNIVERSAL::isa ($rows, 'HASH')) {
277            
278 0         0 foreach my $row_key (keys %$rows) {
279            
280 0         0 my $row = $rows->{$row_key};
281            
282 0         0 $rows->{$row_key} = $rec_pack->new (column_values => $row);
283             }
284             }
285             }
286              
287             our $MAX_LIMIT = 300;
288              
289             sub ordered_list {
290 0     0 0 0 my $self = shift;
291            
292 0         0 my $order = shift;
293 0         0 my $dir = shift;
294 0         0 my $limit = shift;
295 0         0 my $start = shift;
296            
297 0         0 my $filter = shift;
298 0   0     0 my $bind = shift || [];
299            
300 0         0 my $fields = $self->fields;
301            
302 0         0 my $sort_col;
303 0 0       0 if (exists $fields->{$order}) {
    0          
304 0         0 $sort_col = $fields->{$order}->{quoted_column_name};
305             } elsif ($self->_pk_) {
306             # we assume primary key ordering unless ordered column known
307 0         0 $sort_col = $fields->{$self->_pk_}->{quoted_column_name};
308             }
309            
310 0 0       0 if ($dir =~ /^(asc|desc)$/i) {
311 0         0 $dir = lc($1);
312             } else {
313 0         0 $dir = ''; # default sort
314             }
315            
316             # When using LIMIT, it is important to use an ORDER BY clause that
317             # constrains the result rows into a unique order. Otherwise you will
318             # get an unpredictable subset of the query's rows. You might be asking
319             # for the tenth through twentieth rows, but tenth through twentieth
320             # in what ordering? The ordering is unknown, unless you specified ORDER BY.
321 0 0 0     0 if (!$sort_col or $start !~ /\d+/ or $limit !~ /\d+/) {
      0        
322             return {
323 0         0 count => 0,
324             error => "ordering-undefined"
325             };
326             }
327            
328 0         0 $start =~ s/(\d+)/$1/;
329 0         0 $limit =~ s/(\d+)/$1/;
330            
331 0         0 my $count = $self->count ($filter, '', $bind);
332              
333 0 0       0 if ($start > $count) {
334 0         0 $start = $count - $limit;
335 0 0       0 $start = 0 if $start < 0;
336             }
337            
338 0 0 0     0 if ($limit > $MAX_LIMIT or ! $limit > 0) { # try undef -)
339 0         0 $limit = $MAX_LIMIT;
340             }
341              
342 0         0 my $suffix = "order by $sort_col $dir limit $limit offset $start";
343             # debug "suffix: $suffix";
344            
345 0         0 my $list = $self->list ($filter, $suffix, $bind);
346              
347             return {
348 0         0 items => $list,
349             total_count => $count,
350             version => 1,
351             };
352             }
353              
354             # page_size, count, page_num, pages_to_show
355             sub pager {
356 4     4 0 30 my $self = shift;
357 4         6 my $param = shift;
358              
359 4   50     11 my $page_size = $param->{page_size} || 20;
360 4         6 my %pager;
361              
362 4         12 my $number_of_pages = int(($param->{count} + $page_size - 1) / $page_size);
363              
364 4         62 $pager{pager_needed} = ($param->{count} > $page_size);
365            
366 4 50       11 unless ($pager{pager_needed}) {
367 0         0 return;
368             }
369            
370 4   50     13 my $page_number = $param->{page_num} || 0;
371              
372 4   50     10 my $pages_to_show = $param->{pages_to_show} || 10;
373 4         7 my $quarter_to_show = int ($pages_to_show / 4);
374              
375 4         5 my @pages;
376            
377 4 50       12 if ($param->{count} <= $pages_to_show) {
378 0         0 return [1 .. $param->{count}];
379             }
380            
381 4 100       13 if ($page_number <= $quarter_to_show * 2 + 1) {
382             return [
383 3         22 1 .. $quarter_to_show * 3 + 1,
384             undef,
385             $number_of_pages - $quarter_to_show + 1 .. $number_of_pages
386             ];
387             }
388              
389 1 50       7 if ($page_number >= $number_of_pages - ($quarter_to_show * 2 + 1)) {
390             return [
391 0         0 1 .. $quarter_to_show,
392             undef,
393             $number_of_pages - ($quarter_to_show * 3 + 1) .. $number_of_pages
394             ];
395             }
396            
397             return [
398 1         9 1 .. $quarter_to_show,
399             undef,
400             $page_number - $quarter_to_show .. $page_number + $quarter_to_show,
401             undef,
402             $number_of_pages - $quarter_to_show + 1 .. $number_of_pages
403             ];
404            
405             }
406              
407              
408             1;