File Coverage

blib/lib/Jifty/DBI/Collection.pm
Criterion Covered Total %
statement 37 487 7.6
branch 10 210 4.7
condition 1 75 1.3
subroutine 8 67 11.9
pod n/a
total 56 839 6.6


line stmt bran cond sub pod time code
1             package Jifty::DBI::Collection;
2              
3 5     5   296327 use warnings;
  5         7  
  5         183  
4 5     5   21 use strict;
  5         7  
  5         156  
5              
6             =head1 NAME
7              
8             Jifty::DBI::Collection - Encapsulate SQL queries and rows in simple
9             perl objects
10              
11             =head1 SYNOPSIS
12              
13             use Jifty::DBI::Collection;
14            
15             package My::Things;
16             use base qw/Jifty::DBI::Collection/;
17            
18             package main;
19              
20             use Jifty::DBI::Handle;
21             my $handle = Jifty::DBI::Handle->new();
22             $handle->connect( Driver => 'SQLite', Database => "my_test_db" );
23              
24             my $sb = My::Things->new( handle => $handle );
25              
26             $sb->limit( column => "column_1", value => "matchstring" );
27              
28             while ( my $record = $sb->next ) {
29             print $record->my_column_name();
30             }
31              
32             =head1 DESCRIPTION
33              
34             This module provides an object-oriented mechanism for retrieving and
35             updating data in a DBI-accesible database.
36              
37             In order to use this module, you should create a subclass of
38             L<Jifty::DBI::Collection> and a subclass of L<Jifty::DBI::Record> for
39             each table that you wish to access. (See the documentation of
40             L<Jifty::DBI::Record> for more information on subclassing it.)
41              
42             Your L<Jifty::DBI::Collection> subclass must override L</new_item>,
43             and probably should override at least L</_init> also; at the very
44             least, L</_init> should probably call L</_handle> and L</_table> to
45             set the database handle (a L<Jifty::DBI::Handle> object) and table
46             name for the class -- see the L</SYNOPSIS> for an example.
47              
48              
49             =cut
50              
51 5     5   22 use vars qw($VERSION);
  5         12  
  5         236  
52              
53 5     5   2125 use Data::Page;
  5         21587  
  5         24  
54 5     5   1813 use Clone;
  5         9357  
  5         199  
55 5     5   26 use Carp qw/croak/;
  5         5  
  5         202  
56 5     5   18 use base qw/Class::Accessor::Fast/;
  5         7  
  5         25211  
57             __PACKAGE__->mk_accessors(qw/pager preload_columns preload_related/);
58              
59             =head1 METHODS
60              
61             =head2 new
62              
63             Creates a new L<Jifty::DBI::Collection> object and immediately calls
64             L</_init> with the same parameters that were passed to L</new>. If
65             you haven't overridden L<_init> in your subclass, this means that you
66             should pass in a L<Jifty::DBI::Handle> (or one of its subclasses) like
67             this:
68              
69             my $sb = My::Jifty::DBI::Subclass->new( handle => $handle );
70              
71             However, if your subclass overrides L</_init> you do not need to take
72             a handle argument, as long as your subclass takes care of calling the
73             L</_handle> method somehow. This is useful if you want all of your
74             L<Jifty::DBI> objects to use a shared global handle and don't want to
75             have to explicitly pass it in each time, for example.
76              
77             =cut
78              
79             sub new {
80 0     0   0 my $proto = shift;
81 0   0     0 my $class = ref($proto) || $proto;
82 0         0 my $self = {};
83 0         0 bless( $self, $class );
84 0 0       0 $self->record_class( $proto->record_class ) if ref $proto;
85 0         0 $self->_init(@_);
86 0         0 return ($self);
87             }
88              
89             =head2 _init
90              
91             This method is called by L<new> with whatever arguments were passed to
92             L</new>. By default, it takes a C<Jifty::DBI::Handle> object as a
93             C<handle> argument and calls L</_handle> with that.
94              
95             =cut
96              
97             sub _init {
98 0     0   0 my $self = shift;
99 0         0 my %args = (
100             handle => undef,
101             @_
102             );
103 0 0       0 $self->_handle( $args{'handle'} ) if ( $args{'handle'} );
104 0         0 $self->table( $self->new_item->table() );
105 0         0 $self->clean_slate(%args);
106             }
107              
108             sub _init_pager {
109 0     0   0 my $self = shift;
110 0         0 $self->pager( Data::Page->new );
111              
112 0         0 $self->pager->total_entries(0);
113 0         0 $self->pager->entries_per_page(10);
114 0         0 $self->pager->current_page(1);
115             }
116              
117             =head2 clean_slate
118              
119             This completely erases all the data in the object. It's useful if a
120             subclass is doing funky stuff to keep track of a search and wants to
121             reset the object's data without losing its own data; it's probably
122             cleaner to accomplish that in a different way, though.
123              
124             =cut
125              
126             sub clean_slate {
127 0     0   0 my $self = shift;
128 0         0 my %args = (@_);
129 0         0 $self->redo_search();
130 0         0 $self->_init_pager();
131 0         0 $self->{'itemscount'} = 0;
132 0         0 $self->{'tables'} = "";
133 0         0 $self->{'auxillary_tables'} = "";
134 0         0 $self->{'where_clause'} = "";
135 0         0 $self->{'limit_clause'} = "";
136 0         0 $self->{'order'} = "";
137 0         0 $self->{'alias_count'} = 0;
138 0         0 $self->{'first_row'} = 0;
139 0         0 $self->{'show_rows'} = 0;
140 0         0 @{ $self->{'aliases'} } = ();
  0         0  
141              
142 0         0 delete $self->{$_} for qw(
143             items
144             leftjoins
145             raw_rows
146             count_all
147             subclauses
148             restrictions
149             _open_parens
150             );
151              
152 0         0 $self->implicit_clauses(%args);
153 0         0 $self->_is_limited(0);
154             }
155              
156             =head2 implicit_clauses
157              
158             Called by L</clean_slate> to set up any implicit clauses that the
159             collection B<always> has. Defaults to doing nothing. Is passed the
160             paramhash passed into L</new>.
161              
162             =cut
163              
164       0     sub implicit_clauses { }
165              
166             =head2 _handle [DBH]
167              
168             Get or set this object's L<Jifty::DBI::Handle> object.
169              
170             =cut
171              
172             sub _handle {
173 0     0   0 my $self = shift;
174 0 0       0 if (@_) {
175 0         0 $self->{'DBIxhandle'} = shift;
176             }
177 0         0 return ( $self->{'DBIxhandle'} );
178             }
179              
180             =head2 _do_search
181              
182             This internal private method actually executes the search on the
183             database; it is called automatically the first time that you actually
184             need results (such as a call to L</next>).
185              
186             =cut
187              
188             sub _do_search {
189 0     0   0 my $self = shift;
190              
191 0         0 my $query_string = $self->build_select_query();
192              
193             # If we're about to redo the search, we need an empty set of items
194 0         0 delete $self->{'items'};
195              
196 0         0 my $records = $self->_handle->simple_query($query_string);
197 0 0       0 return 0 unless $records;
198 0         0 my @names = @{ $records->{NAME_lc} };
  0         0  
199 0         0 my $data = {};
200 0         0 my $column_map = {};
201 0         0 foreach my $column (@names) {
202 0 0       0 if ($column =~ /^((\w+)_?(?:\d*))_(.*?)$/) {
203 0         0 $column_map->{$1}->{$2} =$column;
204             }
205             }
206 0         0 my @tables = keys %$column_map;
207              
208              
209 0         0 my @order;
210 0         0 while ( my $base_row = $records->fetchrow_hashref() ) {
211 0         0 my $main_pkey = $base_row->{$names[0]};
212 0 0 0     0 push @order, $main_pkey unless ( $order[0] && $order[-1] eq $main_pkey);
213              
214             # let's chop the row into subrows;
215 0         0 foreach my $table (@tables) {
216 0         0 for ( keys %$base_row ) {
217 0 0       0 if ( $_ =~ /$table\_(.*)$/ ) {
218 0   0     0 $data->{$main_pkey}->{$table} ->{ ($base_row->{ $table . '_id' } ||$main_pkey )}->{$1} = $base_row->{$_};
219             }
220             }
221             }
222              
223             }
224              
225             # For related "record" values, we can simply prepopulate the
226             # Jifty::DBI::Record cache and life will be good. (I suspect we want
227             # to do this _before_ doing the initial primary record load on the
228             # off chance that the primary record will try to do the relevant
229             # prefetch manually For related "collection" values, our job is a bit
230             # harder. we need to create a new empty collection object, set it's
231             # "must search" to 0 and manually add the records to it for each of
232             # the items we find. Then we need to ram it into place.
233              
234 0         0 foreach my $row_id ( @order) {
235 0         0 my $item;
236 0         0 foreach my $row ( values %{ $data->{$row_id}->{'main'} } ) {
  0         0  
237 0         0 $item = $self->new_item();
238 0         0 $item->load_from_hash($row);
239             }
240 0         0 foreach my $alias ( grep { $_ ne 'main' } keys %{ $data->{$row_id} } ) {
  0         0  
  0         0  
241              
242 0         0 my $related_rows = $data->{$row_id}->{$alias};
243 0         0 my ( $class, $col_name ) = $self->class_and_column_for_alias($alias);
244 0 0       0 if ($class) {
245              
246 0 0       0 if ( $class->isa('Jifty::DBI::Collection') ) {
    0          
247 0         0 my $collection = $class->new( handle => $self->_handle );
248 0         0 foreach my $row( sort { $a->{id} <=> $b->{id} } values %$related_rows ) {
  0         0  
249 0         0 my $entry
250             = $collection->new_item( handle => $self->_handle );
251 0         0 $entry->load_from_hash($row);
252 0         0 $collection->add_record($entry);
253             }
254              
255 0         0 $item->_prefetched_collection( $col_name => $collection );
256             } elsif ( $class->isa('Jifty::DBI::Record') ) {
257 0         0 foreach my $related_row ( values %$related_rows ) {
258 0         0 my $item = $class->new( handle => $self->_handle );
259 0         0 $item->load_from_hash($related_row);
260             }
261             } else {
262 0         0 Carp::cluck(
263             "Asked to preload $alias as a $class. Don't know how to handle $class"
264             );
265             }
266             }
267              
268              
269             }
270 0         0 $self->add_record($item);
271              
272             }
273 0 0       0 if ( $records->err ) {
274 0         0 $self->{'must_redo_search'} = 0;
275             }
276              
277 0         0 return $self->_record_count;
278             }
279              
280             =head2 add_record RECORD
281              
282             Adds a record object to this collection.
283              
284             This method automatically sets our "must redo search" flag to 0 and our "we have limits" flag to 1.
285              
286             Without those two flags, counting the number of items wouldn't work.
287              
288             =cut
289              
290             sub add_record {
291 0     0   0 my $self = shift;
292 0         0 my $record = shift;
293 0         0 $self->_is_limited(1);
294 0         0 $self->{'must_redo_search'} = 0;
295 0         0 push @{ $self->{'items'} }, $record;
  0         0  
296             }
297              
298             =head2 _record_count
299              
300             This private internal method returns the number of
301             L<Jifty::DBI::Record> objects saved as a result of the last query.
302              
303             =cut
304              
305             sub _record_count {
306 0     0   0 my $self = shift;
307 0 0       0 return 0 unless defined $self->{'items'};
308 0         0 return scalar @{ $self->{'items'} };
  0         0  
309             }
310              
311             =head2 _do_count
312              
313             This internal private method actually executes a counting operation on
314             the database; it is used by L</count> and L</count_all>.
315              
316             =cut
317              
318             sub _do_count {
319 0     0   0 my $self = shift;
320 0   0     0 my $all = shift || 0;
321              
322 0         0 my $query_string = $self->build_select_count_query();
323 0         0 my $records = $self->_handle->simple_query($query_string);
324 0 0       0 return 0 unless $records;
325              
326 0         0 my @row = $records->fetchrow_array();
327 0 0       0 return 0 if $records->err;
328              
329 0 0       0 $self->{ $all ? 'count_all' : 'raw_rows' } = $row[0];
330              
331 0         0 return ( $row[0] );
332             }
333              
334             =head2 _apply_limits STATEMENTREF
335              
336             This routine takes a reference to a scalar containing an SQL
337             statement. It massages the statement to limit the returned rows to
338             only C<< $self->rows_per_page >> rows, skipping C<< $self->first_row >>
339             rows. (That is, if rows are numbered starting from 0, row number
340             C<< $self->first_row >> will be the first row returned.) Note that it
341             probably makes no sense to set these variables unless you are also
342             enforcing an ordering on the rows (with L</order_by_cols>, say).
343              
344             =cut
345              
346             sub _apply_limits {
347 0     0   0 my $self = shift;
348 0         0 my $statementref = shift;
349 0         0 $self->_handle->apply_limits( $statementref, $self->rows_per_page,
350             $self->first_row );
351              
352             }
353              
354             =head2 _distinct_query STATEMENTREF
355              
356             This routine takes a reference to a scalar containing an SQL
357             statement. It massages the statement to ensure a distinct result set
358             is returned.
359              
360             =cut
361              
362             sub _distinct_query {
363 0     0   0 my $self = shift;
364 0         0 my $statementref = shift;
365 0         0 $self->_handle->distinct_query( $statementref, $self );
366             }
367              
368             =head2 _build_joins
369              
370             Build up all of the joins we need to perform this query.
371              
372             =cut
373              
374             sub _build_joins {
375 0     0   0 my $self = shift;
376              
377 0         0 return ( $self->_handle->_build_joins($self) );
378              
379             }
380              
381             =head2 _is_joined
382              
383             Returns true if this collection will be joining multiple tables
384             together.
385              
386             =cut
387              
388             sub _is_joined {
389 0     0   0 my $self = shift;
390 0 0       0 if ( %{ $self->{'leftjoins'} } ) {
  0         0  
391 0         0 return (1);
392             } else {
393 0         0 return ( @{ $self->{'aliases'} } );
  0         0  
394             }
395              
396             }
397              
398             # LIMIT clauses are used for restricting ourselves to subsets of the
399             # search.
400             sub _limit_clause {
401 0     0   0 my $self = shift;
402 0         0 my $limit_clause;
403              
404 0 0       0 if ( $self->rows_per_page ) {
405 0         0 $limit_clause = " LIMIT ";
406 0 0       0 if ( $self->first_row != 0 ) {
407 0         0 $limit_clause .= $self->first_row . ", ";
408             }
409 0         0 $limit_clause .= $self->rows_per_page;
410             } else {
411 0         0 $limit_clause = "";
412             }
413 0         0 return $limit_clause;
414             }
415              
416             =head2 _is_limited
417              
418             If we've limited down this search, return true. Otherwise, return
419             false.
420              
421             =cut
422              
423             sub _is_limited {
424 0     0   0 my $self = shift;
425 0 0       0 if (@_) {
426 0         0 $self->{'is_limited'} = shift;
427             } else {
428 0         0 return ( $self->{'is_limited'} );
429             }
430             }
431              
432             =head2 build_select_query
433              
434             Builds a query string for a "SELECT rows from Tables" statement for
435             this collection
436              
437             =cut
438              
439             sub build_select_query {
440 0     0   0 my $self = shift;
441              
442             # The initial SELECT or SELECT DISTINCT is decided later
443              
444 0         0 my $query_string = $self->_build_joins . " ";
445              
446 0 0       0 if ( $self->_is_limited ) {
447 0         0 $query_string .= $self->_where_clause . " ";
448             }
449 0 0       0 if ( $self->distinct_required ) {
450              
451             # DISTINCT query only required for multi-table selects
452 0         0 $self->_distinct_query( \$query_string );
453             } else {
454 0         0 $query_string
455             = "SELECT " . $self->_preload_columns . " FROM $query_string";
456 0         0 $query_string .= $self->_group_clause;
457 0         0 $query_string .= $self->_order_clause;
458             }
459              
460 0         0 $self->_apply_limits( \$query_string );
461              
462 0         0 return ($query_string)
463              
464             }
465              
466             =head2 preload_columns
467              
468             The columns that the query would load for result items. By default it's everything.
469              
470             XXX TODO: in the case of distinct, it needs to work as well.
471              
472             =cut
473              
474             sub _preload_columns {
475 0     0   0 my $self = shift;
476              
477 0         0 my @cols = ();
478 0         0 my $item = $self->new_item;
479 0 0 0     0 if( $self->{columns} and @{ $self->{columns} } ) {
  0         0  
480 0         0 push @cols, @{$self->{columns}};
  0         0  
481             # push @cols, map { warn "Preloading $_"; "main.$_ as main_" . $_ } @{$preload_columns};
482             } else {
483 0         0 push @cols, $self->_qualified_record_columns( 'main' => $item );
484             }
485 0 0       0 my %preload_related = %{ $self->preload_related || {} };
  0         0  
486 0         0 foreach my $alias ( keys %preload_related ) {
487 0         0 my $related_obj = $preload_related{$alias};
488 0 0       0 if ( my $col_obj = $item->column($related_obj) ) {
489 0         0 my $reference_type = $col_obj->refers_to;
490              
491 0         0 my $reference_item;
492              
493 0 0       0 if ( !$reference_type ) {
    0          
    0          
494 0         0 Carp::cluck(
495             "Asked to prefetch $col_obj->name for $self. But $col_obj->name isn't a known reference"
496             );
497             } elsif ( $reference_type->isa('Jifty::DBI::Collection') ) {
498 0         0 $reference_item = $reference_type->new->new_item();
499             } elsif ( $reference_type->isa('Jifty::DBI::Record') ) {
500 0         0 $reference_item = $reference_type->new;
501             } else {
502 0         0 Carp::cluck(
503             "Asked to prefetch $col_obj->name for $self. But $col_obj->name isn't a known type"
504             );
505             }
506              
507 0         0 push @cols,
508             $self->_qualified_record_columns( $alias => $reference_item );
509             }
510              
511             # push @cols, map { $_ . ".*" } keys %{ $self->preload_related || {} };
512              
513             }
514 0         0 return CORE::join( ', ', @cols );
515             }
516              
517             =head2 class_and_column_for_alias
518              
519             Takes the alias you've assigned to a prefetched related object. Returns the class
520             of the column we've declared that alias preloads.
521              
522             =cut
523              
524             sub class_and_column_for_alias {
525 0     0   0 my $self = shift;
526 0         0 my $alias = shift;
527 0 0       0 my %preload_related = %{ $self->preload_related || {} };
  0         0  
528 0         0 my $related_colname = $preload_related{$alias};
529 0 0       0 if ( my $col_obj = $self->new_item->column($related_colname) ) {
530 0         0 return ( $col_obj->refers_to => $related_colname );
531             }
532 0         0 return undef;
533             }
534              
535             sub _qualified_record_columns {
536 0     0   0 my $self = shift;
537 0         0 my $alias = shift;
538 0         0 my $item = shift;
539 0         0 grep {$_} map {
540 0         0 my $col = $_;
  0         0  
541 0 0       0 if ( $col->virtual ) {
542 0         0 undef;
543             } else {
544 0         0 $col = $col->name;
545 0         0 $alias . "." . $col . " as " . $alias . "_" . $col;
546             }
547             } $item->columns;
548             }
549              
550             =head2 prefetch ALIAS_NAME ATTRIBUTE
551              
552             prefetches all related rows from alias ALIAS_NAME into the record attribute ATTRIBUTE of the
553             sort of item this collection is.
554              
555             If you have employees who have many phone numbers, this method will let you search for all your employees
556             and prepopulate their phone numbers.
557              
558             Right now, in order to make this work, you need to do an explicit join between your primary table and the subsidiary tables AND then specify the name of the attribute you want to prefetch related data into.
559             This method could be a LOT smarter. since we already know what the relationships between our tables are, that could all be precomputed.
560              
561             XXX TODO: in the future, this API should be extended to let you specify columns.
562              
563             =cut
564              
565             sub prefetch {
566 0     0   0 my $self = shift;
567 0         0 my $alias = shift;
568 0         0 my $into_attribute = shift;
569              
570 0   0     0 my $preload_related = $self->preload_related() || {};
571              
572 0         0 $preload_related->{$alias} = $into_attribute;
573              
574 0         0 $self->preload_related($preload_related);
575              
576             }
577              
578             =head2 distinct_required
579              
580             Returns true if Jifty::DBI expects that this result set will end up
581             with repeated rows and should be "condensed" down to a single row for
582             each unique primary key.
583              
584             Out of the box, this method returns true if you've joined to another table.
585             To add additional logic, feel free to override this method in your subclass.
586              
587             XXX TODO: it should be possible to create a better heuristic than the simple
588             "is it joined?" question we're asking now. Something along the lines of "are we
589             joining this table to something that is not the other table's primary key"
590              
591             =cut
592              
593             sub distinct_required {
594 0     0   0 my $self = shift;
595 0 0       0 return( $self->_is_joined ? 1 : 0 );
596             }
597              
598             =head2 build_select_count_query
599              
600             Builds a SELECT statement to find the number of rows this collection
601             would find.
602              
603             =cut
604              
605             sub build_select_count_query {
606 0     0   0 my $self = shift;
607              
608 0         0 my $query_string = $self->_build_joins . " ";
609              
610 0 0       0 if ( $self->_is_limited ) {
611 0         0 $query_string .= $self->_where_clause . " ";
612             }
613              
614             # DISTINCT query only required for multi-table selects
615 0 0       0 if ( $self->_is_joined ) {
616 0         0 $query_string = $self->_handle->distinct_count( \$query_string );
617             } else {
618 0         0 $query_string = "SELECT count(main.id) FROM " . $query_string;
619             }
620              
621 0         0 return ($query_string);
622             }
623              
624             =head2 do_search
625              
626             C<Jifty::DBI::Collection> usually does searches "lazily". That is, it
627             does a C<SELECT COUNT> or a C<SELECT> on the fly the first time you ask
628             for results that would need one or the other. Sometimes, you need to
629             display a count of results found before you iterate over a collection,
630             but you know you're about to do that too. To save a bit of wear and tear
631             on your database, call C<do_search> before that C<count>.
632              
633             =cut
634              
635             sub do_search {
636 0     0   0 my $self = shift;
637 0 0       0 $self->_do_search() if $self->{'must_redo_search'};
638              
639             }
640              
641             =head2 next
642              
643             Returns the next row from the set as an object of the type defined by
644             sub new_item. When the complete set has been iterated through,
645             returns undef and resets the search such that the following call to
646             L</next> will start over with the first item retrieved from the
647             database.
648              
649             =cut
650              
651             sub next {
652 0     0   0 my $self = shift;
653 0         0 my @row;
654              
655 0 0       0 return (undef) unless ( $self->_is_limited );
656              
657 0 0       0 $self->_do_search() if $self->{'must_redo_search'};
658              
659 0 0       0 if ( $self->{'itemscount'} < $self->_record_count )
660             { #return the next item
661 0         0 my $item = ( $self->{'items'}[ $self->{'itemscount'} ] );
662 0         0 $self->{'itemscount'}++;
663 0         0 return ($item);
664             } else { #we've gone through the whole list. reset the count.
665 0         0 $self->goto_first_item();
666 0         0 return (undef);
667             }
668             }
669              
670             =head2 goto_first_item
671              
672             Starts the recordset counter over from the first item. The next time
673             you call L</next>, you'll get the first item returned by the database,
674             as if you'd just started iterating through the result set.
675              
676             =cut
677              
678             sub goto_first_item {
679 0     0   0 my $self = shift;
680 0         0 $self->goto_item(0);
681             }
682              
683             =head2 goto_item
684              
685             Takes an integer, n. Sets the record counter to n. the next time you
686             call L</next>, you'll get the nth item.
687              
688             =cut
689              
690             sub goto_item {
691 0     0   0 my $self = shift;
692 0         0 my $item = shift;
693 0         0 $self->{'itemscount'} = $item;
694             }
695              
696             =head2 first
697              
698             Returns the first item
699              
700             =cut
701              
702             sub first {
703 0     0   0 my $self = shift;
704 0         0 $self->goto_first_item();
705 0         0 return ( $self->next );
706             }
707              
708             =head2 last
709              
710             Returns the last item
711              
712             =cut
713              
714             sub last {
715 0     0   0 my $self = shift;
716 0         0 $self->goto_item( ( $self->count ) - 1 );
717 0         0 return ( $self->next );
718             }
719              
720             =head2 items_array_ref
721              
722             Return a reference to an array containing all objects found by this
723             search.
724              
725             =cut
726              
727             sub items_array_ref {
728 0     0   0 my $self = shift;
729              
730             # If we're not limited, return an empty array
731 0 0       0 return [] unless $self->_is_limited;
732              
733             # Do a search if we need to.
734 0 0       0 $self->_do_search() if $self->{'must_redo_search'};
735              
736             # If we've got any items in the array, return them. Otherwise,
737             # return an empty array
738 0   0     0 return ( $self->{'items'} || [] );
739             }
740              
741             sub new_item {
742 0     0   0 my $self = shift;
743 0         0 my $class = $self->record_class();
744              
745 0 0       0 die "Jifty::DBI::Collection needs to be subclassed; override new_item\n"
746             unless $class;
747              
748 0         0 $class->require();
749 0         0 return $class->new( handle => $self->_handle );
750             }
751              
752             =head2 record_class
753              
754             Returns the record class which this is a collection of; override this
755             to subclass. Or, pass it the name of a class an an argument after
756             creating a C<Jifty::DBI::Collection> object to create an 'anonymous'
757             collection class.
758              
759             If you haven't specified a record class, this returns a best guess at
760             the name of the record class for this collection.
761              
762             It uses a simple heuristic to determine the record class name -- It
763             chops "Collection" off its own name. If you want to name your records
764             and collections differently, go right ahead, but don't say we didn't
765             warn you.
766              
767             =cut
768              
769             sub record_class {
770 0     0   0 my $self = shift;
771 0 0       0 if (@_) {
    0          
772 0 0       0 $self->{record_class} = shift if (@_);
773             } elsif ( not $self->{record_class} ) {
774 0         0 my $class = ref($self);
775 0 0       0 $class =~ s/Collection$//
776             or die "Can't guess record class from $class";
777 0         0 $self->{record_class} = $class;
778             }
779 0         0 return $self->{record_class};
780             }
781              
782             =head2 redo_search
783              
784             Takes no arguments. Tells Jifty::DBI::Collection that the next time
785             it's asked for a record, it should requery the database
786              
787             =cut
788              
789             sub redo_search {
790 0     0   0 my $self = shift;
791 0         0 $self->{'must_redo_search'} = 1;
792 0         0 delete $self->{$_} for qw(items raw_rows count_all);
793 0         0 $self->{'itemscount'} = 0;
794             }
795              
796             =head2 unlimit
797              
798             Clears all restrictions and causes this object to return all
799             rows in the primary table.
800              
801             =cut
802              
803             sub unlimit {
804 0     0   0 my $self = shift;
805              
806 0         0 $self->clean_slate();
807 0         0 $self->_is_limited(-1);
808             }
809              
810             =head2 limit
811              
812             Takes a hash of parameters with the following keys:
813              
814             =over 4
815              
816             =item table
817              
818             Can be set to something different than this table if a join is
819             wanted (that means we can't do recursive joins as for now).
820              
821             =item alias
822              
823             Unless alias is set, the join criterias will be taken from EXT_LINKcolumn
824             and INT_LINKcolumn and added to the criterias. If alias is set, new
825             criterias about the foreign table will be added.
826              
827             =item column
828              
829             Column to be checked against.
830              
831             =item value
832              
833             Should always be set and will always be quoted. If the value is a
834             subclass of Jifty::DBI::Object, the value will be interpreted to be
835             the object's id.
836              
837             =item operator
838              
839             operator is the SQL operator to use for this phrase. Possible choices include:
840              
841             =over 4
842              
843             =item "="
844              
845             =item "!="
846              
847             Any other standard SQL comparision operators that your underlying
848             database supports are also valid.
849              
850             =item "LIKE"
851              
852             =item "NOT LIKE"
853              
854             =item "MATCHES"
855              
856             MATCHES is like LIKE, except it surrounds the value with % signs.
857              
858             =item "STARTSWITH"
859              
860             STARTSWITH is like LIKE, except it only appends a % at the end of the string
861              
862             =item "ENDSWITH"
863              
864             ENDSWITH is like LIKE, except it prepends a % to the beginning of the string
865              
866             =back
867              
868             =item entry_aggregator
869              
870             Can be AND or OR (or anything else valid to aggregate two clauses in SQL)
871              
872             =item case_sensitive
873              
874             on some databases, such as postgres, setting case_sensitive to 1 will make
875             this search case sensitive. Note that this flag is ignored if the column
876             is numeric.
877              
878             =back
879              
880             =cut
881              
882             sub limit {
883 0     0   0 my $self = shift;
884 0         0 my %args = (
885             table => $self->table,
886             column => undef,
887             value => undef,
888             alias => undef,
889             quote_value => 1,
890             entry_aggregator => 'or',
891             case_sensitive => undef,
892             operator => '=',
893             subclause => undef,
894             leftjoin => undef,
895             @_ # get the real argumentlist
896             );
897              
898 0         0 my ($Alias);
899              
900             # We need to be passed a column and a value, at very least
901             croak "Must provide a column to limit"
902 0 0       0 unless defined $args{column};
903             croak "Must provide a value to limit to"
904 0 0       0 unless defined $args{value};
905              
906             # make passing in an object DTRT
907 0 0 0     0 if ( ref( $args{value} ) && $args{value}->isa('Jifty::DBI::Record') ) {
908 0         0 $args{value} = $args{value}->id;
909             }
910              
911             #since we're changing the search criteria, we need to redo the search
912 0         0 $self->redo_search();
913              
914 0 0       0 if ( $args{'column'} ) {
915              
916             #If it's a like, we supply the %s around the search term
917 0 0       0 if ( $args{'operator'} =~ /LIKE/i ) {
    0          
    0          
    0          
918 0         0 $args{'value'} = $args{'value'};
919             } elsif ( $args{'operator'} =~ /MATCHES/i ) {
920 0         0 $args{'value'} = "%" . $args{'value'} . "%";
921 0         0 $args{'operator'} = "LIKE";
922             } elsif ( $args{'operator'} =~ /STARTSWITH/i ) {
923 0         0 $args{'value'} = $args{'value'} . "%";
924 0         0 $args{'operator'} = "LIKE";
925             } elsif ( $args{'operator'} =~ /ENDSWITH/i ) {
926 0         0 $args{'value'} = "%" . $args{'value'};
927 0         0 $args{'operator'} = "LIKE";
928             }
929              
930             #if we're explicitly told not to to quote the value or
931             # we're doing an IS or IS NOT (null), don't quote the operator.
932              
933 0 0 0     0 if ( $args{'quote_value'} && $args{'operator'} !~ /IS/i ) {
934 0         0 my $tmp = $self->_handle->dbh->quote( $args{'value'} );
935              
936             # Accomodate DBI drivers that don't understand UTF8
937 0 0       0 if ( $] >= 5.007 ) {
938 0         0 require Encode;
939 0 0       0 if ( Encode::is_utf8( $args{'value'} ) ) {
940 0         0 Encode::_utf8_on($tmp);
941             }
942             }
943 0         0 $args{'value'} = $tmp;
944             }
945             }
946              
947 0         0 my ( $Clause, $qualified_column );
948              
949             #TODO: $args{'value'} should take an array of values and generate
950             # the proper where clause.
951              
952             #If we're performing a left join, we really want the alias to be the
953             #left join criterion.
954              
955 0 0 0     0 if ( ( defined $args{'leftjoin'} )
956             && ( not defined $args{'alias'} ) )
957             {
958 0         0 $args{'alias'} = $args{'leftjoin'};
959             }
960              
961             # {{{ if there's no alias set, we need to set it
962              
963 0 0       0 unless ( $args{'alias'} ) {
964              
965             #if the table we're looking at is the same as the main table
966 0 0       0 if ( $args{'table'} eq $self->table ) {
967              
968             # TODO this code assumes no self joins on that table.
969             # if someone can name a case where we'd want to do that,
970             # I'll change it.
971              
972 0         0 $args{'alias'} = 'main';
973             }
974              
975             else {
976 0         0 $args{'alias'} = $self->new_alias( $args{'table'} );
977             }
978             }
979              
980             # }}}
981              
982             # Set this to the name of the column and the alias, unless we've been
983             # handed a subclause name
984              
985 0         0 $qualified_column = $args{'alias'} . "." . $args{'column'};
986              
987 0 0       0 if ( $args{'subclause'} ) {
988 0         0 $Clause = $args{'subclause'};
989             } else {
990 0         0 $Clause = $qualified_column;
991             }
992              
993 0 0       0 warn "$self->_generic_restriction qualified_column=$qualified_column\n"
994             if ( $self->DEBUG );
995              
996 0         0 my ($restriction);
997              
998             # If we're trying to get a leftjoin restriction, lets set
999             # $restriction to point htere. otherwise, lets construct normally
1000              
1001 0 0       0 if ( $args{'leftjoin'} ) {
1002             $restriction = \$self->{'leftjoins'}{ $args{'leftjoin'} }{'criteria'}
1003 0         0 {"$Clause"};
1004             } else {
1005 0         0 $restriction = \$self->{'restrictions'}{"$Clause"};
1006             }
1007              
1008             # If it's a new value or we're overwriting this sort of restriction,
1009              
1010 0 0 0     0 if ( $self->_handle->case_sensitive
      0        
      0        
1011             && defined $args{'value'}
1012             && $args{'quote_value'}
1013             && !$args{'case_sensitive'} )
1014             {
1015              
1016             # don't worry about case for numeric columns_in_db
1017 0         0 my $column_obj = $self->new_item()->column( $args{column} );
1018 0 0       0 if ( defined $column_obj ? !$column_obj->is_numeric : 1 ) {
    0          
1019             ( $qualified_column, $args{'operator'}, $args{'value'} )
1020             = $self->_handle->_make_clause_case_insensitive(
1021 0         0 $qualified_column, $args{'operator'}, $args{'value'} );
1022             }
1023             }
1024              
1025 0         0 my $clause = "($qualified_column $args{'operator'} $args{'value'})";
1026              
1027             # Juju because this should come _AFTER_ the EA
1028 0         0 my $prefix = "";
1029 0 0       0 if ( $self->{_open_parens}{$Clause} ) {
1030 0         0 $prefix = " ( " x $self->{_open_parens}{$Clause};
1031 0         0 delete $self->{_open_parens}{$Clause};
1032             }
1033              
1034 0 0 0     0 if (( ( exists $args{'entry_aggregator'} )
      0        
      0        
1035             and ( $args{'entry_aggregator'} || "" ) eq 'none'
1036             )
1037             or ( !$$restriction )
1038             )
1039             {
1040              
1041 0         0 $$restriction = $prefix . $clause;
1042              
1043             } else {
1044 0         0 $$restriction .= $args{'entry_aggregator'} . $prefix . $clause;
1045             }
1046              
1047             # We're now limited. people can do searches.
1048              
1049 0         0 $self->_is_limited(1);
1050              
1051 0 0       0 if ( defined( $args{'alias'} ) ) {
1052 0         0 return ( $args{'alias'} );
1053             } else {
1054 0         0 return (1);
1055             }
1056             }
1057              
1058             =head2 open_paren CLAUSE
1059              
1060             Places an open paren at the current location in the given C<CLAUSE>.
1061             Note that this can be used for Deep Magic, and has a high likelyhood
1062             of allowing you to construct malformed SQL queries. Its interface
1063             will probably change in the near future, but its presence allows for
1064             arbitrarily complex queries.
1065              
1066             =cut
1067              
1068             sub open_paren {
1069 0     0   0 my ( $self, $clause ) = @_;
1070 0         0 $self->{_open_parens}{$clause}++;
1071             }
1072              
1073             =head2 close_paren CLAUSE
1074              
1075             Places a close paren at the current location in the given C<CLAUSE>.
1076             Note that this can be used for Deep Magic, and has a high likelyhood
1077             of allowing you to construct malformed SQL queries. Its interface
1078             will probably change in the near future, but its presence allows for
1079             arbitrarily complex queries.
1080              
1081             =cut
1082              
1083             # Immediate Action
1084             sub close_paren {
1085 0     0   0 my ( $self, $clause ) = @_;
1086 0         0 my $restriction = \$self->{'restrictions'}{"$clause"};
1087 0 0       0 if ( !$$restriction ) {
1088 0         0 $$restriction = " ) ";
1089             } else {
1090 0         0 $$restriction .= " ) ";
1091             }
1092             }
1093              
1094             sub _add_subclause {
1095 0     0   0 my $self = shift;
1096 0         0 my $clauseid = shift;
1097 0         0 my $subclause = shift;
1098              
1099 0         0 $self->{'subclauses'}{"$clauseid"} = $subclause;
1100              
1101             }
1102              
1103             sub _where_clause {
1104 0     0   0 my $self = shift;
1105 0         0 my $where_clause = '';
1106              
1107             # Go through all the generic restrictions and build up the
1108             # "generic_restrictions" subclause. That's the only one that the
1109             # collection builds itself. Arguably, the abstraction should be
1110             # better, but I don't really see where to put it.
1111 0         0 $self->_compile_generic_restrictions();
1112              
1113             #Go through all restriction types. Build the where clause from the
1114             #Various subclauses.
1115              
1116 0         0 my @subclauses;
1117 0         0 foreach my $subclause ( sort keys %{ $self->{'subclauses'} } ) {
  0         0  
1118 0         0 push @subclauses, $self->{'subclauses'}{"$subclause"};
1119             }
1120              
1121 0 0       0 $where_clause = " WHERE " . CORE::join( ' AND ', @subclauses )
1122             if (@subclauses);
1123              
1124 0         0 return ($where_clause);
1125              
1126             }
1127              
1128             #Compile the restrictions to a WHERE Clause
1129              
1130             sub _compile_generic_restrictions {
1131 0     0   0 my $self = shift;
1132 0         0 my ($restriction);
1133              
1134 0         0 delete $self->{'subclauses'}{'generic_restrictions'};
1135              
1136             #Go through all the restrictions of this type. Buld up the generic subclause
1137 0         0 foreach $restriction ( sort keys %{ $self->{'restrictions'} } ) {
  0         0  
1138 0 0       0 if ( defined $self->{'subclauses'}{'generic_restrictions'} ) {
1139 0         0 $self->{'subclauses'}{'generic_restrictions'} .= " AND ";
1140             }
1141             $self->{'subclauses'}{'generic_restrictions'}
1142 0         0 .= "(" . $self->{'restrictions'}{"$restriction"} . ")";
1143             }
1144             }
1145              
1146             # set $self->{$type .'_clause'} to new value
1147             # redo_search only if new value is really new
1148             sub _set_clause {
1149 0     0   0 my $self = shift;
1150 0         0 my ( $type, $value ) = @_;
1151 0         0 $type .= '_clause';
1152 0 0 0     0 if ( ( $self->{$type} || '' ) ne ( $value || '' ) ) {
      0        
1153 0         0 $self->redo_search;
1154             }
1155 0         0 $self->{$type} = $value;
1156             }
1157              
1158             =head2 order_by_cols DEPRECATED
1159              
1160             *DEPRECATED*. Use C<order_by> method.
1161              
1162             =cut
1163              
1164             sub order_by_cols {
1165 0     0   0 require Carp;
1166 0         0 Carp::cluck("order_by_cols is deprecated, use order_by method");
1167 0         0 goto &order_by;
1168             }
1169              
1170             =head2 order_by EMPTY|HASH|ARRAY_OF_HASHES
1171              
1172             Orders the returned results by column(s) and/or function(s) on column(s).
1173              
1174             Takes a paramhash of C<alias>, C<column> and C<order>
1175             or C<function> and C<order>.
1176             C<alias> defaults to main.
1177             C<order> defaults to ASC(ending), DES(cending) is also a valid value.
1178             C<column> and C<function> have no default values.
1179              
1180             Use C<function> instead of C<alias> and C<column> to order by
1181             the function value. Note that if you want use a column as argument of
1182             the function then you have to build correct reference with alias
1183             in the C<alias.column> format.
1184              
1185             Use array of hashes to order by many columns/functions.
1186              
1187             The results would be unordered if method called without arguments.
1188              
1189             Returns the current list of columns.
1190              
1191             =cut
1192              
1193             sub order_by {
1194 0     0   0 my $self = shift;
1195 0 0       0 if (@_) {
1196 0         0 my @args = @_;
1197              
1198 0 0       0 unless ( UNIVERSAL::isa( $args[0], 'HASH' ) ) {
1199 0         0 @args = {@args};
1200             }
1201 0         0 $self->{'order_by'} = \@args;
1202 0         0 $self->redo_search();
1203             }
1204 0   0     0 return ( $self->{'order_by'} || []);
1205             }
1206              
1207             =head2 _order_clause
1208              
1209             returns the ORDER BY clause for the search.
1210              
1211             =cut
1212              
1213             sub _order_clause {
1214 3     3   153 my $self = shift;
1215              
1216 3 50       23 return '' unless $self->{'order_by'};
1217              
1218 3         4 my $clause = '';
1219 3         3 foreach my $row ( @{ $self->{'order_by'} } ) {
  3         8  
1220              
1221 9         36 my %rowhash = (
1222             alias => 'main',
1223             column => undef,
1224             order => 'ASC',
1225             %$row
1226             );
1227 9 100       28 if ( $rowhash{'order'} =~ /^des/i ) {
1228 3         5 $rowhash{'order'} = "DESC";
1229             } else {
1230 6         7 $rowhash{'order'} = "ASC";
1231             }
1232              
1233 9 50 33     37 if ( $rowhash{'function'} ) {
    50          
1234 0 0       0 $clause .= ( $clause ? ", " : " " );
1235 0         0 $clause .= $rowhash{'function'} . ' ';
1236 0         0 $clause .= $rowhash{'order'};
1237              
1238             } elsif ( (defined $rowhash{'alias'} )
1239             and ( $rowhash{'column'} ) )
1240             {
1241              
1242 9 100       15 $clause .= ( $clause ? ", " : " " );
1243 9 100       18 $clause .= $rowhash{'alias'} . "." if $rowhash{'alias'};
1244 9         11 $clause .= $rowhash{'column'} . " ";
1245 9         16 $clause .= $rowhash{'order'};
1246             }
1247             }
1248 3 50       12 $clause = " ORDER BY$clause " if $clause;
1249 3         15 return $clause;
1250             }
1251              
1252             =head2 group_by_cols DEPRECATED
1253              
1254             *DEPRECATED*. Use group_by method.
1255              
1256             =cut
1257              
1258             sub group_by_cols {
1259 0     0     require Carp;
1260 0           Carp::cluck("group_by_cols is deprecated, use group_by method");
1261 0           goto &group_by;
1262             }
1263              
1264             =head2 group_by EMPTY|HASH|ARRAY_OF_HASHES
1265              
1266             Groups the search results by column(s) and/or function(s) on column(s).
1267              
1268             Takes a paramhash of C<alias> and C<column> or C<function>.
1269             C<alias> defaults to main.
1270             C<column> and C<function> have no default values.
1271              
1272             Use C<function> instead of C<alias> and C<column> to group by
1273             the function value. Note that if you want use a column as argument
1274             of the function then you have to build correct reference with alias
1275             in the C<alias.column> format.
1276              
1277             Use array of hashes to group by many columns/functions.
1278              
1279             The method is EXPERIMENTAL and subject to change.
1280              
1281             =cut
1282              
1283             sub group_by {
1284 0     0     my $self = shift;
1285              
1286 0           my @args = @_;
1287              
1288 0 0         unless ( UNIVERSAL::isa( $args[0], 'HASH' ) ) {
1289 0           @args = {@args};
1290             }
1291 0           $self->{'group_by'} = \@args;
1292 0           $self->redo_search();
1293             }
1294              
1295             =head2 _group_clause
1296              
1297             Private function to return the "GROUP BY" clause for this query.
1298              
1299             =cut
1300              
1301             sub _group_clause {
1302 0     0     my $self = shift;
1303 0 0         return '' unless $self->{'group_by'};
1304              
1305 0           my $row;
1306             my $clause;
1307              
1308 0           foreach $row ( @{ $self->{'group_by'} } ) {
  0            
1309 0           my %rowhash = (
1310             alias => 'main',
1311              
1312             column => undef,
1313             %$row
1314             );
1315 0 0 0       if ( $rowhash{'function'} ) {
    0          
1316 0 0         $clause .= ( $clause ? ", " : " " );
1317 0           $clause .= $rowhash{'function'};
1318              
1319             } elsif ( ( $rowhash{'alias'} )
1320             and ( $rowhash{'column'} ) )
1321             {
1322              
1323 0 0         $clause .= ( $clause ? ", " : " " );
1324 0           $clause .= $rowhash{'alias'} . ".";
1325 0           $clause .= $rowhash{'column'};
1326             }
1327             }
1328 0 0         if ($clause) {
1329 0           return " GROUP BY" . $clause . " ";
1330             } else {
1331 0           return '';
1332             }
1333             }
1334              
1335             =head2 new_alias table_OR_CLASS
1336              
1337             Takes the name of a table or a Jifty::DBI::Record subclass.
1338             Returns the string of a new Alias for that table, which can be used
1339             to Join tables or to limit what gets found by
1340             a search.
1341              
1342             =cut
1343              
1344             sub new_alias {
1345 0     0     my $self = shift;
1346 0   0       my $refers_to = shift || die "Missing parameter";
1347 0           my $table;
1348              
1349 0 0         if ( $refers_to->can('table') ) {
1350 0           $table = $refers_to->table;
1351             } else {
1352 0           $table = $refers_to;
1353             }
1354              
1355 0           my $alias = $self->_get_alias($table);
1356              
1357 0           my $subclause = "$table $alias";
1358              
1359 0           push( @{ $self->{'aliases'} }, $subclause );
  0            
1360              
1361 0           return $alias;
1362             }
1363              
1364             # _get_alias is a private function which takes an tablename and
1365             # returns a new alias for that table without adding something
1366             # to self->{'aliases'}. This function is used by new_alias
1367             # and the as-yet-unnamed left join code
1368              
1369             sub _get_alias {
1370 0     0     my $self = shift;
1371 0           my $table = shift;
1372              
1373 0           $self->{'alias_count'}++;
1374 0           my $alias = $table . "_" . $self->{'alias_count'};
1375              
1376 0           return ($alias);
1377              
1378             }
1379              
1380             =head2 join
1381              
1382             Join instructs Jifty::DBI::Collection to join two tables.
1383              
1384             The standard form takes a param hash with keys C<alias1>, C<column1>, C<alias2>
1385             and C<column2>. C<alias1> and C<alias2> are column aliases obtained from
1386             $self->new_alias or a $self->limit. C<column1> and C<column2> are the columns
1387             in C<alias1> and C<alias2> that should be linked, respectively. For this
1388             type of join, this method has no return value.
1389              
1390             Supplying the parameter C<type> => 'left' causes Join to perform a left
1391             join. in this case, it takes C<alias1>, C<column1>, C<table2> and
1392             C<column2>. Because of the way that left joins work, this method needs a
1393             table for the second column rather than merely an alias. For this type
1394             of join, it will return the alias generated by the join.
1395              
1396             The parameter C<operator> defaults C<=>, but you can specify other
1397             operators to join with.
1398              
1399             Instead of C<alias1>/C<column1>, it's possible to specify expression, to join
1400             C<alias2>/C<table2> on an arbitrary expression.
1401              
1402             =cut
1403              
1404             sub join {
1405 0     0     my $self = shift;
1406 0           my %args = (
1407             type => 'normal',
1408             column1 => undef,
1409             alias1 => 'main',
1410             table2 => undef,
1411             column2 => undef,
1412             alias2 => undef,
1413             @_
1414             );
1415              
1416 0           $self->_handle->join( collection => $self, %args );
1417              
1418             }
1419              
1420             =head2 set_page_info [per_page => NUMBER,] [current_page => NUMBER]
1421              
1422             Sets the current page (one-based) and number of items per page on the
1423             pager object, and pulls the number of elements from the collection.
1424             This both sets up the collection's L<Data::Page> object so that you
1425             can use its calculations, and sets the L<Jifty::DBI::Collection>
1426             C<first_row> and C<rows_per_page> so that queries return values from
1427             the selected page.
1428              
1429             =cut
1430              
1431             sub set_page_info {
1432 0     0     my $self = shift;
1433 0           my %args = (
1434             per_page => undef,
1435             current_page => undef, # 1-based
1436             @_
1437             );
1438              
1439             $self->pager->total_entries( $self->count_all )
1440             ->entries_per_page( $args{'per_page'} )
1441 0           ->current_page( $args{'current_page'} );
1442              
1443 0           $self->rows_per_page( $args{'per_page'} );
1444 0   0       $self->first_row( $self->pager->first || 1 );
1445              
1446             }
1447              
1448             =head2 rows_per_page
1449              
1450             limits the number of rows returned by the database. Optionally, takes
1451             an integer which restricts the # of rows returned in a result Returns
1452             the number of rows the database should display.
1453              
1454             =cut
1455              
1456             sub rows_per_page {
1457 0     0     my $self = shift;
1458 0 0         $self->{'show_rows'} = shift if (@_);
1459              
1460 0           return ( $self->{'show_rows'} );
1461             }
1462              
1463             =head2 first_row
1464              
1465             Get or set the first row of the result set the database should return.
1466             Takes an optional single integer argrument. Returns the currently set
1467             integer first row that the database should return.
1468              
1469              
1470             =cut
1471              
1472             # returns the first row
1473             sub first_row {
1474 0     0     my $self = shift;
1475 0 0         if (@_) {
1476 0           $self->{'first_row'} = shift;
1477              
1478             #SQL starts counting at 0
1479 0           $self->{'first_row'}--;
1480              
1481             #gotta redo the search if changing pages
1482 0           $self->redo_search();
1483             }
1484 0           return ( $self->{'first_row'} );
1485             }
1486              
1487             =head2 _items_counter
1488              
1489             Returns the current position in the record set.
1490              
1491             =cut
1492              
1493             sub _items_counter {
1494 0     0     my $self = shift;
1495 0           return $self->{'itemscount'};
1496             }
1497              
1498             =head2 count
1499              
1500             Returns the number of records in the set.
1501              
1502             =cut
1503              
1504             sub count {
1505 0     0     my $self = shift;
1506              
1507             # An unlimited search returns no tickets
1508 0 0         return 0 unless ( $self->_is_limited );
1509              
1510             # If we haven't actually got all objects loaded in memory, we
1511             # really just want to do a quick count from the database.
1512 0 0         if ( $self->{'must_redo_search'} ) {
1513              
1514             # If we haven't already asked the database for the row count, do that
1515 0 0         $self->_do_count unless ( $self->{'raw_rows'} );
1516              
1517             #Report back the raw # of rows in the database
1518 0           return ( $self->{'raw_rows'} );
1519             }
1520              
1521             # If we have loaded everything from the DB we have an
1522             # accurate count already.
1523             else {
1524 0           return $self->_record_count;
1525             }
1526             }
1527              
1528             =head2 count_all
1529              
1530             Returns the total number of potential records in the set, ignoring any
1531             limit_clause.
1532              
1533             =cut
1534              
1535             # 22:24 [Robrt(500@outer.space)] It has to do with Caching.
1536             # 22:25 [Robrt(500@outer.space)] The documentation says it ignores the limit.
1537             # 22:25 [Robrt(500@outer.space)] But I don't believe thats true.
1538             # 22:26 [msg(Robrt)] yeah. I
1539             # 22:26 [msg(Robrt)] yeah. I'm not convinced it does anything useful right now
1540             # 22:26 [msg(Robrt)] especially since until a week ago, it was setting one variable and returning another
1541             # 22:27 [Robrt(500@outer.space)] I remember.
1542             # 22:27 [Robrt(500@outer.space)] It had to do with which Cached value was returned.
1543             # 22:27 [msg(Robrt)] (given that every time we try to explain it, we get it Wrong)
1544             # 22:27 [Robrt(500@outer.space)] Because Count can return a different number than actual NumberOfResults
1545             # 22:28 [msg(Robrt)] in what case?
1546             # 22:28 [Robrt(500@outer.space)] count_all _always_ used the return value of _do_count(), as opposed to Count which would return the cached number of
1547             # results returned.
1548             # 22:28 [Robrt(500@outer.space)] IIRC, if you do a search with a limit, then raw_rows will == limit.
1549             # 22:31 [msg(Robrt)] ah.
1550             # 22:31 [msg(Robrt)] that actually makes sense
1551             # 22:31 [Robrt(500@outer.space)] You should paste this conversation into the count_all docs.
1552             # 22:31 [msg(Robrt)] perhaps I'll create a new method that _actually_ do that.
1553             # 22:32 [msg(Robrt)] since I'm not convinced it's been doing that correctly
1554              
1555             sub count_all {
1556 0     0     my $self = shift;
1557              
1558             # An unlimited search returns no tickets
1559 0 0         return 0 unless ( $self->_is_limited );
1560              
1561             # If we haven't actually got all objects loaded in memory, we
1562             # really just want to do a quick count from the database.
1563 0 0 0       if ( $self->{'must_redo_search'} || !$self->{'count_all'} ) {
1564              
1565             # If we haven't already asked the database for the row count, do that
1566 0 0         $self->_do_count(1) unless ( $self->{'count_all'} );
1567              
1568             #Report back the raw # of rows in the database
1569 0           return ( $self->{'count_all'} );
1570             }
1571              
1572             # If we have loaded everything from the DB we have an
1573             # accurate count already.
1574             else {
1575 0           return $self->_record_count;
1576             }
1577             }
1578              
1579             =head2 is_last
1580              
1581             Returns true if the current row is the last record in the set.
1582              
1583             =cut
1584              
1585             sub is_last {
1586 0     0     my $self = shift;
1587              
1588 0 0         return undef unless $self->count;
1589              
1590 0 0         if ( $self->_items_counter == $self->count ) {
1591 0           return (1);
1592             } else {
1593 0           return (0);
1594             }
1595             }
1596              
1597             sub DEBUG {
1598 0     0     my $self = shift;
1599 0 0         if (@_) {
1600 0           $self->{'DEBUG'} = shift;
1601             }
1602 0           return ( $self->{'DEBUG'} );
1603             }
1604              
1605             =head2 column
1606              
1607             Normally a collection object contains record objects populated with all columns
1608             in the database, but you can restrict the records to only contain some
1609             particular columns, by calling the C<column> method once for each column you
1610             are interested in.
1611              
1612             Takes a hash of parameters; the C<column>, C<table> and C<alias> keys means
1613             the same as in the C<limit> method. A special C<function> key may contain
1614             one of several possible kinds of expressions:
1615              
1616             =over 4
1617              
1618             =item C<DISTINCT COUNT>
1619              
1620             Same as C<COUNT(DISTINCT ?)>.
1621              
1622             =item Expression with C<?> in it
1623              
1624             The C<?> is substituted with the column name, then passed verbatim to the
1625             underlying C<SELECT> statement.
1626              
1627             =item Expression with C<(> in it
1628              
1629             The expression is passed verbatim to the underlying C<SELECT>.
1630              
1631             =item Any other expression
1632              
1633             The expression is taken to be a function name. For example, C<SUM> means
1634             the same thing as C<SUM(?)>.
1635              
1636             =back
1637              
1638             =cut
1639              
1640             sub column {
1641 0     0     my $self = shift;
1642 0           my %args = (
1643             table => undef,
1644             alias => undef,
1645             column => undef,
1646             function => undef,
1647             @_
1648             );
1649              
1650 0   0       my $table = $args{table} || do {
1651             if ( my $alias = $args{alias} ) {
1652             $alias =~ s/_\d+$//;
1653             $alias;
1654             } else {
1655             $self->table;
1656             }
1657             };
1658              
1659 0   0       my $name = ( $args{alias} || 'main' ) . '.' . $args{column};
1660 0 0         if ( my $func = $args{function} ) {
1661 0 0         if ( $func =~ /^DISTINCT\s*COUNT$/i ) {
    0          
    0          
1662 0           $name = "COUNT(DISTINCT $name)";
1663             }
1664              
1665             # If we want to substitute
1666             elsif ( $func =~ /\?/ ) {
1667 0           $name =~ s/\?/$name/g;
1668             }
1669              
1670             # If we want to call a simple function on the column
1671             elsif ( $func !~ /\(/ ) {
1672 0           $name = "\U$func\E($name)";
1673             } else {
1674 0           $name = $func;
1675             }
1676              
1677             }
1678              
1679 0   0       my $column = "col" . @{ $self->{columns} ||= [] };
  0            
1680 0 0 0       $column = $args{column} if $table eq $self->table and !$args{alias};
1681 0   0       $column = ($args{'alias'}||'main')."_".$column;
1682 0           push @{ $self->{columns} }, "$name AS \L$column";
  0            
1683 0           return $column;
1684             }
1685              
1686             =head2 columns LIST
1687              
1688             Specify that we want to load only the columns in LIST, which is a
1689              
1690             =cut
1691              
1692             sub columns {
1693 0     0     my $self = shift;
1694 0           $self->column( column => $_ ) for @_;
1695             }
1696              
1697             =head2 columns_in_db table
1698              
1699             Return a list of columns in table, lowercased.
1700              
1701             TODO: Why are they lowercased?
1702              
1703             =cut
1704              
1705             sub columns_in_db {
1706             my $self = shift;
1707             my $table = shift;
1708              
1709             my $dbh = $self->_handle->dbh;
1710              
1711             # TODO: memoize this
1712              
1713             return map lc( $_->[0] ), @{
1714             eval {
1715             $dbh->column_info( '', '', $table, '' )->fetchall_arrayref( [3] );
1716             }
1717             || $dbh->selectall_arrayref("DESCRIBE $table;")
1718             || $dbh->selectall_arrayref("DESCRIBE \u$table;")
1719             || []
1720             };
1721             }
1722              
1723             =head2 has_column { table => undef, column => undef }
1724              
1725             Returns true if table has column column.
1726             Return false otherwise
1727              
1728             =cut
1729              
1730             sub has_column {
1731             my $self = shift;
1732             my %args = (
1733             column => undef,
1734             table => undef,
1735             @_
1736             );
1737              
1738             my $table = $args{table} or die;
1739             my $column = $args{column} or die;
1740             return grep { $_ eq $column } $self->columns_in_db($table);
1741             }
1742              
1743             =head2 table [table]
1744              
1745             If called with an argument, sets this collection's table.
1746              
1747             Always returns this collection's table.
1748              
1749             =cut
1750              
1751             sub table {
1752             my $self = shift;
1753             $self->{table} = shift if (@_);
1754             return $self->{table};
1755             }
1756              
1757             =head2 clone
1758              
1759             Returns copy of the current object with all search restrictions.
1760              
1761             =cut
1762              
1763             sub clone {
1764             my $self = shift;
1765              
1766             my $obj = bless {}, ref($self);
1767             %$obj = %$self;
1768              
1769             $obj->redo_search(); # clean out the object of data
1770              
1771             $obj->{$_} = Clone::clone( $obj->{$_} ) for ( $self->_cloned_attributes );
1772             return $obj;
1773             }
1774              
1775             =head2 _cloned_attributes
1776              
1777             Returns list of the object's fields that should be copied.
1778              
1779             If your subclass store references in the object that should be copied while
1780             clonning then you probably want override this method and add own values to
1781             the list.
1782              
1783             =cut
1784              
1785             sub _cloned_attributes {
1786             return qw(
1787             aliases
1788             left_joins
1789             subclauses
1790             restrictions
1791             );
1792             }
1793              
1794             1;
1795             __END__
1796              
1797              
1798              
1799             =head1 TESTING
1800              
1801             In order to test most of the features of C<Jifty::DBI::Collection>,
1802             you need to provide C<make test> with a test database. For each DBI
1803             driver that you would like to test, set the environment variables
1804             C<JDBI_TEST_FOO>, C<JDBI_TEST_FOO_USER>, and C<JDBI_TEST_FOO_PASS> to a
1805             database name, database username, and database password, where "FOO"
1806             is the driver name in all uppercase. You can test as many drivers as
1807             you like. (The appropriate C<DBD::> module needs to be installed in
1808             order for the test to work.) Note that the C<SQLite> driver will
1809             automatically be tested if C<DBD::Sqlite> is installed, using a
1810             temporary file as the database. For example:
1811              
1812             JDBI_TEST_MYSQL=test JDBI_TEST_MYSQL_USER=root JDBI_TEST_MYSQL_PASS=foo \
1813             JDBI_TEST_PG=test JDBI_TEST_PG_USER=postgres make test
1814              
1815              
1816             =head1 AUTHOR
1817              
1818             Copyright (c) 2001-2005 Jesse Vincent, jesse@fsck.com.
1819              
1820             All rights reserved.
1821              
1822             This library is free software; you can redistribute it
1823             and/or modify it under the same terms as Perl itself.
1824              
1825              
1826             =head1 SEE ALSO
1827              
1828             Jifty::DBI::Handle, Jifty::DBI::Record.
1829              
1830             =cut
1831