| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Jifty::DBI::Collection; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 13 |  |  | 13 |  | 70733 | use warnings; | 
|  | 13 |  |  |  |  | 29 |  | 
|  | 13 |  |  |  |  | 574 |  | 
| 4 | 13 |  |  | 13 |  | 73 | use strict; | 
|  | 13 |  |  |  |  | 35 |  | 
|  | 13 |  |  |  |  | 705 |  | 
| 5 | 13 |  |  | 13 |  | 4856 | use Scalar::Defer qw/lazy/; | 
|  | 13 |  |  |  |  | 81109 |  | 
|  | 13 |  |  |  |  | 133 |  | 
| 6 | 13 |  |  | 13 |  | 1301 | use Scalar::Util qw/weaken/; | 
|  | 13 |  |  |  |  | 31 |  | 
|  | 13 |  |  |  |  | 1439 |  | 
| 7 |  |  |  |  |  |  | use overload ( | 
| 8 | 435 |  |  | 435 |  | 84680 | '@{}'       => \&items_array_ref, | 
| 9 |  |  |  |  |  |  | '<>'        => \&next, | 
| 10 |  |  |  |  |  |  | bool        => sub {shift}, | 
| 11 | 13 |  |  |  |  | 251 | fallback    => 1 | 
| 12 | 13 |  |  | 13 |  | 77 | ); | 
|  | 13 |  |  |  |  | 27 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | =head1 NAME | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | Jifty::DBI::Collection - Encapsulate SQL queries and rows in simple | 
| 17 |  |  |  |  |  |  | perl objects | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | use Jifty::DBI::Collection; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | package My::ThingCollection; | 
| 24 |  |  |  |  |  |  | use base qw/Jifty::DBI::Collection/; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | package My::Thing; | 
| 27 |  |  |  |  |  |  | use Jifty::DBI::Schema; | 
| 28 |  |  |  |  |  |  | use Jifty::DBI::Record schema { | 
| 29 |  |  |  |  |  |  | column column_1 => type is 'text'; | 
| 30 |  |  |  |  |  |  | }; | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | package main; | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | use Jifty::DBI::Handle; | 
| 35 |  |  |  |  |  |  | my $handle = Jifty::DBI::Handle->new(); | 
| 36 |  |  |  |  |  |  | $handle->connect( driver => 'SQLite', database => "my_test_db" ); | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | my $collection = My::ThingCollection->new( handle => $handle ); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | $collection->limit( column => "column_1", value => "matchstring" ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | while ( my $record = $collection->next ) { | 
| 43 |  |  |  |  |  |  | print $record->id; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | This module provides an object-oriented mechanism for retrieving and | 
| 49 |  |  |  |  |  |  | updating data in a DBI-accessible database. | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | In order to use this module, you should create a subclass of | 
| 52 |  |  |  |  |  |  | L and a subclass of L for | 
| 53 |  |  |  |  |  |  | each table that you wish to access.  (See the documentation of | 
| 54 |  |  |  |  |  |  | L for more information on subclassing it.) | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | Your L subclass must override L, | 
| 57 |  |  |  |  |  |  | and probably should override at least L also; at the very | 
| 58 |  |  |  |  |  |  | least, L should probably call L and L to | 
| 59 |  |  |  |  |  |  | set the database handle (a L object) and table | 
| 60 |  |  |  |  |  |  | name for the class -- see the L for an example. | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | =cut | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 13 |  |  | 13 |  | 1349 | use vars qw($VERSION); | 
|  | 13 |  |  |  |  | 26 |  | 
|  | 13 |  |  |  |  | 617 |  | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 13 |  |  | 13 |  | 13552 | use Data::Page; | 
|  | 13 |  |  |  |  | 553860 |  | 
|  | 13 |  |  |  |  | 160 |  | 
| 68 | 13 |  |  | 13 |  | 19141 | use Clone; | 
|  | 13 |  |  |  |  | 292666 |  | 
|  | 13 |  |  |  |  | 914 |  | 
| 69 | 13 |  |  | 13 |  | 138 | use Carp qw/croak/; | 
|  | 13 |  |  |  |  | 30 |  | 
|  | 13 |  |  |  |  | 776 |  | 
| 70 | 13 |  |  | 13 |  | 88 | use base qw/Class::Accessor::Fast/; | 
|  | 13 |  |  |  |  | 31 |  | 
|  | 13 |  |  |  |  | 158572 |  | 
| 71 |  |  |  |  |  |  | __PACKAGE__->mk_accessors(qw/pager prefetch_related derived _handle _is_limited rows_per_page/); | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | =head1 METHODS | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | =head2 new | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | Creates a new L object and immediately calls | 
| 78 |  |  |  |  |  |  | L with the same parameters that were passed to L.  If | 
| 79 |  |  |  |  |  |  | you haven't overridden L<_init> in your subclass, this means that you | 
| 80 |  |  |  |  |  |  | should pass in a L (or one of its subclasses) like | 
| 81 |  |  |  |  |  |  | this: | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | my $collection = My::Jifty::DBI::Subclass->new( handle => $handle ); | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | However, if your subclass overrides L you do not need to take | 
| 86 |  |  |  |  |  |  | a handle argument, as long as your subclass takes care of calling the | 
| 87 |  |  |  |  |  |  | L method somehow.  This is useful if you want all of your | 
| 88 |  |  |  |  |  |  | L objects to use a shared global handle and don't want to | 
| 89 |  |  |  |  |  |  | have to explicitly pass it in each time, for example. | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | =cut | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | sub new { | 
| 94 | 230 |  |  | 230 | 1 | 126873 | my $proto = shift; | 
| 95 | 230 |  | 66 |  |  | 1451 | my $class = ref($proto) || $proto; | 
| 96 | 230 |  |  |  |  | 674 | my $self  = {}; | 
| 97 | 230 |  |  |  |  | 770 | bless( $self, $class ); | 
| 98 | 230 | 100 |  |  |  | 749 | $self->record_class( $proto->record_class ) if ref $proto; | 
| 99 | 230 |  |  |  |  | 725 | $self->_init(@_); | 
| 100 | 230 |  |  |  |  | 3517 | return ($self); | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | =head2 _init | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | This method is called by L with whatever arguments were passed to | 
| 106 |  |  |  |  |  |  | L.  By default, it takes a C object as a | 
| 107 |  |  |  |  |  |  | C argument and calls L with that. | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | =cut | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | sub _init { | 
| 112 | 230 |  |  | 230 |  | 454 | my $self = shift; | 
| 113 | 230 |  |  |  |  | 1100 | my %args = ( | 
| 114 |  |  |  |  |  |  | handle  => undef, | 
| 115 |  |  |  |  |  |  | derived => undef, | 
| 116 |  |  |  |  |  |  | @_ | 
| 117 |  |  |  |  |  |  | ); | 
| 118 | 230 | 50 |  |  |  | 1440 | $self->_handle( $args{'handle'} )  if ( $args{'handle'} ); | 
| 119 | 230 | 100 |  |  |  | 4973 | $self->derived( $args{'derived'} ) if ( $args{'derived'} ); | 
| 120 | 230 |  |  |  |  | 683 | $self->table( $self->record_class->table() ); | 
| 121 | 230 |  |  |  |  | 1373 | $self->clean_slate(%args); | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | sub _init_pager { | 
| 125 | 296 |  |  | 296 |  | 417 | my $self = shift; | 
| 126 | 296 |  |  |  |  | 1591 | return $self->pager( Data::Page->new(0, 10, 1) ); | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | =head2 clean_slate | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | This completely erases all the data in the object. It's useful if a | 
| 132 |  |  |  |  |  |  | subclass is doing funky stuff to keep track of a search and wants to | 
| 133 |  |  |  |  |  |  | reset the object's data without losing its own data; it's probably | 
| 134 |  |  |  |  |  |  | cleaner to accomplish that in a different way, though. | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | =cut | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub clean_slate { | 
| 139 | 296 |  |  | 296 | 1 | 17446 | my $self = shift; | 
| 140 | 296 |  |  |  |  | 800 | my %args = (@_); | 
| 141 | 296 |  |  |  |  | 1020 | $self->redo_search(); | 
| 142 | 296 |  |  |  |  | 11494 | $self->_init_pager(); | 
| 143 | 296 |  |  |  |  | 18941 | $self->{'itemscount'}       = 0; | 
| 144 | 296 |  |  |  |  | 798 | $self->{'tables'}           = ""; | 
| 145 | 296 |  |  |  |  | 900 | $self->{'auxillary_tables'} = ""; | 
| 146 | 296 |  |  |  |  | 578 | $self->{'where_clause'}     = ""; | 
| 147 | 296 |  |  |  |  | 697 | $self->{'limit_clause'}     = ""; | 
| 148 | 296 |  |  |  |  | 514 | $self->{'order'}            = ""; | 
| 149 | 296 |  |  |  |  | 943 | $self->{'alias_count'}      = 0; | 
| 150 | 296 |  |  |  |  | 572 | $self->{'first_row'}        = 0; | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 296 |  |  |  |  | 2743 | delete $self->{$_} for qw( | 
| 153 |  |  |  |  |  |  | items | 
| 154 |  |  |  |  |  |  | joins | 
| 155 |  |  |  |  |  |  | raw_rows | 
| 156 |  |  |  |  |  |  | count_all | 
| 157 |  |  |  |  |  |  | subclauses | 
| 158 |  |  |  |  |  |  | restrictions | 
| 159 |  |  |  |  |  |  | _open_parens | 
| 160 |  |  |  |  |  |  | criteria_count | 
| 161 |  |  |  |  |  |  | ); | 
| 162 |  |  |  |  |  |  |  | 
| 163 | 296 |  |  |  |  | 1343 | $self->rows_per_page(0); | 
| 164 | 296 |  |  |  |  | 5627 | $self->implicit_clauses(%args); | 
| 165 | 296 |  |  |  |  | 1371 | $self->_is_limited(0); | 
| 166 |  |  |  |  |  |  | } | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | =head2 implicit_clauses | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | Called by L to set up any implicit clauses that the | 
| 171 |  |  |  |  |  |  | collection B has.  Defaults to doing nothing. Is passed the | 
| 172 |  |  |  |  |  |  | paramhash passed into L. | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | =cut | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 296 |  |  | 296 | 1 | 623 | sub implicit_clauses { } | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | =head2 _handle [DBH] | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | Get or set this object's L object. | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | =cut | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | =head2 _do_search | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | This internal private method actually executes the search on the | 
| 187 |  |  |  |  |  |  | database; it is called automatically the first time that you actually | 
| 188 |  |  |  |  |  |  | need results (such as a call to L). | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | =cut | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | sub _do_search { | 
| 193 | 48 |  |  | 48 |  | 97 | my $self = shift; | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 48 |  |  |  |  | 207 | my $query_string = $self->build_select_query(); | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | # If we're about to redo the search, we need an empty set of items | 
| 198 | 48 |  |  |  |  | 127 | delete $self->{'items'}; | 
| 199 |  |  |  |  |  |  |  | 
| 200 | 48 |  |  |  |  | 150 | my $records = $self->_handle->simple_query($query_string); | 
| 201 | 48 | 50 |  |  |  | 287 | return 0 unless $records; | 
| 202 | 48 |  |  |  |  | 77 | my @names = @{ $records->{NAME_lc} }; | 
|  | 48 |  |  |  |  | 1244 |  | 
| 203 | 48 |  |  |  |  | 201 | my $data  = {}; | 
| 204 |  |  |  |  |  |  |  | 
| 205 | 48 | 100 |  |  |  | 87 | my @tables = map { $_->{alias} } values %{ $self->prefetch_related || {} }; | 
|  | 3 |  |  |  |  | 100 |  | 
|  | 48 |  |  |  |  | 1327 |  | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 48 | 100 |  |  |  | 564 | unless ( @tables ) { | 
| 208 | 45 |  |  |  |  | 1263 | while ( my $row = $records->fetchrow_hashref() ) { | 
| 209 |  |  |  |  |  |  | $row->{ substr($_, 5) } = delete $row->{ $_ } | 
| 210 | 130 |  |  |  |  | 2996 | foreach grep rindex($_, "main_", 0) == 0, keys %$row; | 
| 211 | 130 |  |  |  |  | 503 | my $item = $self->new_item; | 
| 212 | 130 |  |  |  |  | 515 | $item->load_from_hash($row, fast => 1); | 
| 213 | 130 |  |  |  |  | 355 | $self->add_record($item); | 
| 214 |  |  |  |  |  |  | } | 
| 215 | 45 | 50 |  |  |  | 282 | if ( $records->err ) { | 
| 216 | 0 |  |  |  |  | 0 | $self->{'must_redo_search'} = 0; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 | 45 |  |  |  |  | 162 | return $self->_record_count; | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  |  | 
| 222 | 3 |  |  |  |  | 4 | my @order; | 
| 223 | 3 |  |  |  |  | 4 | my $i = 1; | 
| 224 | 3 |  |  |  |  | 80 | while ( my $base_row = $records->fetchrow_hashref() ) { | 
| 225 | 18 |  |  |  |  | 28 | my $main_pkey = $base_row->{ $names[0] }; | 
| 226 | 18 | 50 |  |  |  | 129 | $main_pkey = 'unique-'.$i++ if $self->{group_by}; | 
| 227 | 18 | 100 | 100 |  |  | 87 | push @order, $main_pkey | 
| 228 |  |  |  |  |  |  | unless ( $order[0] && $order[-1] eq $main_pkey ); | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | # let's chop the row into subrows; | 
| 231 | 18 |  |  |  |  | 32 | foreach my $table ('main', @tables) { | 
| 232 | 36 |  |  |  |  | 49 | my %tmp = (); | 
| 233 | 36 |  |  |  |  | 284 | for my $k( grep rindex($_, $table ."_", 0) == 0, keys %$base_row ) { | 
| 234 | 90 |  |  |  |  | 564 | $tmp{ substr($k, length($table)+1) } = $base_row->{ $k }; | 
| 235 |  |  |  |  |  |  | } | 
| 236 | 36 | 50 | 33 |  |  | 586 | $data->{$main_pkey}{$table}{ $base_row->{ $table . '_id' } || $main_pkey } | 
| 237 |  |  |  |  |  |  | = \%tmp if keys %tmp; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 3 |  |  |  |  | 8 | foreach my $row_id (@order) { | 
| 242 | 10 |  |  |  |  | 14 | my $item; | 
| 243 | 10 |  |  |  |  | 13 | foreach my $row ( values %{ $data->{$row_id}->{'main'} } ) { | 
|  | 10 |  |  |  |  | 101 |  | 
| 244 | 10 |  |  |  |  | 31 | $item = $self->new_item(); | 
| 245 | 10 |  |  |  |  | 87 | $item->load_from_hash($row, fast => 1); | 
| 246 |  |  |  |  |  |  | } | 
| 247 | 10 |  |  |  |  | 17 | foreach my $alias ( grep { $_ ne 'main' } keys %{ $data->{$row_id} } ) | 
|  | 20 |  |  |  |  | 69 |  | 
|  | 10 |  |  |  |  | 26 |  | 
| 248 |  |  |  |  |  |  | { | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 10 |  |  |  |  | 19 | my $related_rows = $data->{$row_id}->{$alias}; | 
| 251 | 10 |  |  |  |  | 35 | my ( $class, $col_name ) | 
| 252 |  |  |  |  |  |  | = $self->class_and_column_for_alias($alias); | 
| 253 | 10 | 50 |  |  |  | 23 | next unless $class; | 
| 254 |  |  |  |  |  |  |  | 
| 255 | 12 |  |  |  |  | 23 | my @rows = sort { $a->{id} <=> $b->{id} } | 
|  | 18 |  |  |  |  | 49 |  | 
| 256 | 10 |  |  |  |  | 22 | grep { $_->{id} } values %$related_rows; | 
| 257 |  |  |  |  |  |  |  | 
| 258 | 10 | 100 |  |  |  | 485 | if ( $class->isa('Jifty::DBI::Collection') ) { | 
|  |  | 50 |  |  |  |  |  | 
| 259 | 4 |  |  |  |  | 17 | my $collection = $class->new( $self->_new_collection_args, | 
| 260 |  |  |  |  |  |  | derived => 1 ); | 
| 261 | 4 |  |  |  |  | 241 | foreach my $row (@rows) { | 
| 262 | 12 |  |  |  |  | 323 | my $entry = $collection->new_item; | 
| 263 | 12 |  |  |  |  | 43 | $entry->load_from_hash($row, fast => 1); | 
| 264 | 12 |  |  |  |  | 35 | $collection->add_record($entry); | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 4 |  |  |  |  | 20 | $item->prefetched( $col_name => $collection ); | 
| 268 |  |  |  |  |  |  | } elsif ( $class->isa('Jifty::DBI::Record') ) { | 
| 269 | 6 | 50 |  |  |  | 16 | warn "Multiple rows returned for $class in prefetch" | 
| 270 |  |  |  |  |  |  | if @rows > 1; | 
| 271 | 6 |  |  |  |  | 15 | my $entry = $class->new( $self->_new_record_args ); | 
| 272 | 6 | 50 |  |  |  | 28 | $entry->load_from_hash( shift(@rows), fast => 1 ) if @rows; | 
| 273 | 6 |  |  |  |  | 24 | $item->prefetched( $col_name => $entry ); | 
| 274 |  |  |  |  |  |  | } else { | 
| 275 | 0 |  |  |  |  | 0 | Carp::cluck( | 
| 276 |  |  |  |  |  |  | "Asked to prefetch $alias as a $class. Don't know how to handle $class" | 
| 277 |  |  |  |  |  |  | ); | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  | } | 
| 280 | 10 |  |  |  |  | 35 | $self->add_record($item); | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | } | 
| 283 | 3 | 50 |  |  |  | 32 | if ( $records->err ) { | 
| 284 | 0 |  |  |  |  | 0 | $self->{'must_redo_search'} = 0; | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 3 |  |  |  |  | 17 | return $self->_record_count; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | sub _new_record_args { | 
| 291 | 192 |  |  | 192 |  | 300 | my $self = shift; | 
| 292 | 192 |  |  |  |  | 886 | return ( handle => $self->_handle ); | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | sub _new_collection_args { | 
| 296 | 5 |  |  | 5 |  | 6 | my $self = shift; | 
| 297 | 5 |  |  |  |  | 14 | return ( handle => $self->_handle ); | 
| 298 |  |  |  |  |  |  | } | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | =head2 add_record RECORD | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | Adds a record object to this collection. | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | This method automatically sets our "must redo search" flag to 0 and our "we have limits" flag to 1. | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | Without those two flags, counting the number of items wouldn't work. | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | =cut | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | sub add_record { | 
| 311 | 152 |  |  | 152 | 1 | 207 | my $self   = shift; | 
| 312 | 152 |  |  |  |  | 191 | my $record = shift; | 
| 313 | 152 |  |  |  |  | 481 | $self->_is_limited(1); | 
| 314 | 152 |  |  |  |  | 996 | $self->{'must_redo_search'} = 0; | 
| 315 | 152 |  |  |  |  | 178 | push @{ $self->{'items'} }, $record; | 
|  | 152 |  |  |  |  | 3637 |  | 
| 316 |  |  |  |  |  |  | } | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | =head2 _record_count | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | This private internal method returns the number of | 
| 321 |  |  |  |  |  |  | L objects saved as a result of the last query. | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | =cut | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | sub _record_count { | 
| 326 | 240 |  |  | 240 |  | 19607 | my $self = shift; | 
| 327 | 240 | 100 |  |  |  | 2307 | return 0 unless defined $self->{'items'}; | 
| 328 | 220 |  |  |  |  | 260 | return scalar @{ $self->{'items'} }; | 
|  | 220 |  |  |  |  | 1477 |  | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | =head2 _do_count | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | This internal private method actually executes a counting operation on | 
| 334 |  |  |  |  |  |  | the database; it is used by L and L. | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | =cut | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | sub _do_count { | 
| 339 | 78 |  |  | 78 |  | 141 | my $self = shift; | 
| 340 | 78 |  | 50 |  |  | 329 | my $all = shift || 0; | 
| 341 |  |  |  |  |  |  |  | 
| 342 | 78 |  |  |  |  | 325 | my $query_string = $self->build_select_count_query(); | 
| 343 | 78 |  |  |  |  | 350 | my $records      = $self->_handle->simple_query($query_string); | 
| 344 | 78 | 50 |  |  |  | 217 | return 0 unless $records; | 
| 345 |  |  |  |  |  |  |  | 
| 346 | 78 |  |  |  |  | 1428 | my @row = $records->fetchrow_array(); | 
| 347 | 78 | 50 |  |  |  | 1750 | return 0 if $records->err; | 
| 348 |  |  |  |  |  |  |  | 
| 349 | 78 | 50 |  |  |  | 376 | $self->{ $all ? 'count_all' : 'raw_rows' } = $row[0]; | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 78 |  |  |  |  | 1731 | return ( $row[0] ); | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | =head2 _apply_limits STATEMENTREF | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | This routine takes a reference to a scalar containing an SQL | 
| 357 |  |  |  |  |  |  | statement.  It massages the statement to limit the returned rows to | 
| 358 |  |  |  |  |  |  | only C<< $self->rows_per_page >> rows, skipping C<< $self->first_row >> | 
| 359 |  |  |  |  |  |  | rows.  (That is, if rows are numbered starting from 0, row number | 
| 360 |  |  |  |  |  |  | C<< $self->first_row >> will be the first row returned.)  Note that it | 
| 361 |  |  |  |  |  |  | probably makes no sense to set these variables unless you are also | 
| 362 |  |  |  |  |  |  | enforcing an ordering on the rows (with L, say). | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | =cut | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | sub _apply_limits { | 
| 367 | 246 |  |  | 246 |  | 352 | my $self         = shift; | 
| 368 | 246 |  |  |  |  | 282 | my $statementref = shift; | 
| 369 | 246 |  |  |  |  | 1039 | $self->_handle->apply_limits( $statementref, $self->rows_per_page, | 
| 370 |  |  |  |  |  |  | $self->first_row ); | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | } | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | =head2 _distinct_query STATEMENTREF | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | This routine takes a reference to a scalar containing an SQL | 
| 377 |  |  |  |  |  |  | statement.  It massages the statement to ensure a distinct result set | 
| 378 |  |  |  |  |  |  | is returned. | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | =cut | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | sub _distinct_query { | 
| 383 | 5 |  |  | 5 |  | 8 | my $self         = shift; | 
| 384 | 5 |  |  |  |  | 23 | my $statementref = shift; | 
| 385 | 5 |  |  |  |  | 16 | $self->_handle->distinct_query( $statementref, $self ); | 
| 386 |  |  |  |  |  |  | } | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | =head2 _build_joins | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | Build up all of the joins we need to perform this query. | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | =cut | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | sub _build_joins { | 
| 395 | 328 |  |  | 328 |  | 445 | my $self = shift; | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 328 |  |  |  |  | 1108 | return ( $self->_handle->_build_joins($self) ); | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | =head2 _is_joined | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | Returns true if this collection will be joining multiple tables | 
| 404 |  |  |  |  |  |  | together. | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | =cut | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | sub _is_joined { | 
| 409 | 333 |  |  | 333 |  | 11723 | my $self = shift; | 
| 410 | 333 | 100 | 100 |  |  | 1154 | if ( $self->{'joins'} && keys %{ $self->{'joins'} } ) { | 
|  | 325 |  |  |  |  | 1397 |  | 
| 411 | 19 |  |  |  |  | 89 | return (1); | 
| 412 |  |  |  |  |  |  | } else { | 
| 413 | 314 |  |  |  |  | 1353 | return 0; | 
| 414 |  |  |  |  |  |  | } | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | =head2 _is_distinctly_joined | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | Returns true if this collection is joining multiple table, but is | 
| 420 |  |  |  |  |  |  | joining other table's distinct fields, hence resulting in distinct | 
| 421 |  |  |  |  |  |  | resultsets.  The behaviour is undefined if called on a non-joining | 
| 422 |  |  |  |  |  |  | collection. | 
| 423 |  |  |  |  |  |  |  | 
| 424 |  |  |  |  |  |  | =cut | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | sub _is_distinctly_joined { | 
| 427 | 18 |  |  | 18 |  | 33 | my $self = shift; | 
| 428 | 18 | 50 |  |  |  | 62 | if ( $self->{'joins'} ) { | 
| 429 | 18 |  |  |  |  | 25 | for ( values %{ $self->{'joins'} } ) { | 
|  | 18 |  |  |  |  | 51 |  | 
| 430 | 18 | 100 |  |  |  | 96 | return 0 unless $_->{is_distinct}; | 
| 431 |  |  |  |  |  |  | } | 
| 432 |  |  |  |  |  |  |  | 
| 433 | 4 |  |  |  |  | 25 | return 1; | 
| 434 |  |  |  |  |  |  | } | 
| 435 |  |  |  |  |  |  | } | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | =head2 _is_limited | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | If we've limited down this search, return true. Otherwise, return | 
| 440 |  |  |  |  |  |  | false. | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | C<1> means "we have limits" | 
| 443 |  |  |  |  |  |  | C<-1> means "we should return all rows. We want no where clause" | 
| 444 |  |  |  |  |  |  | C<0> means "no limits have been applied yet. | 
| 445 |  |  |  |  |  |  |  | 
| 446 |  |  |  |  |  |  | =cut | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | =head2 build_select_query | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | Builds a query string for a "SELECT rows from Tables" statement for | 
| 451 |  |  |  |  |  |  | this collection | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | =cut | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | sub build_select_query { | 
| 456 | 246 |  |  | 246 | 1 | 972 | my $self = shift; | 
| 457 |  |  |  |  |  |  |  | 
| 458 | 246 | 50 |  |  |  | 838 | return "" if $self->derived; | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | # The initial SELECT or SELECT DISTINCT is decided later | 
| 461 |  |  |  |  |  |  |  | 
| 462 | 246 |  |  |  |  | 1667 | my $query_string = $self->_build_joins . " "; | 
| 463 |  |  |  |  |  |  |  | 
| 464 | 246 | 50 |  |  |  | 905 | if ( $self->_is_limited ) { | 
| 465 | 246 |  |  |  |  | 1749 | $query_string .= $self->_where_clause . " "; | 
| 466 |  |  |  |  |  |  | } | 
| 467 | 246 | 100 |  |  |  | 962 | if ( $self->distinct_required ) { | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | # DISTINCT query only required for multi-table selects | 
| 470 | 5 |  |  |  |  | 31 | $self->_distinct_query( \$query_string ); | 
| 471 |  |  |  |  |  |  | } else { | 
| 472 | 241 |  |  |  |  | 624 | $query_string | 
| 473 |  |  |  |  |  |  | = "SELECT " . $self->query_columns . " FROM $query_string"; | 
| 474 | 241 |  |  |  |  | 986 | $query_string .= $self->_group_clause; | 
| 475 | 241 |  |  |  |  | 674 | $query_string .= $self->_order_clause; | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  |  | 
| 478 | 246 |  |  |  |  | 755 | $self->_apply_limits( \$query_string ); | 
| 479 |  |  |  |  |  |  |  | 
| 480 | 246 |  |  |  |  | 2213 | return ($query_string) | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | } | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | =head2 query_columns | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | The columns that the query would load for result items.  By default | 
| 487 |  |  |  |  |  |  | it's everything. | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =cut | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | sub query_columns { | 
| 492 | 246 |  |  | 246 | 1 | 346 | my $self = shift; | 
| 493 |  |  |  |  |  |  |  | 
| 494 | 246 |  |  |  |  | 385 | my @cols = (); | 
| 495 | 246 | 100 | 66 |  |  | 926 | if ( $self->{columns} and @{ $self->{columns} } ) { | 
|  | 3 |  |  |  |  | 15 |  | 
| 496 | 3 |  |  |  |  | 5 | push @cols, @{ $self->{columns} }; | 
|  | 3 |  |  |  |  | 9 |  | 
| 497 |  |  |  |  |  |  | } else { | 
| 498 | 243 |  |  |  |  | 590 | push @cols, $self->_qualified_record_columns( 'main' => $self->record_class ); | 
| 499 |  |  |  |  |  |  | } | 
| 500 | 246 | 100 |  |  |  | 426 | my %prefetch_related = %{ $self->prefetch_related || {} }; | 
|  | 246 |  |  |  |  | 742 |  | 
| 501 | 246 |  |  |  |  | 3010 | foreach my $alias ( keys %prefetch_related ) { | 
| 502 | 3 |  |  |  |  | 8 | my $class = $prefetch_related{$alias}{class}; | 
| 503 |  |  |  |  |  |  |  | 
| 504 | 3 |  |  |  |  | 3 | my $reference; | 
| 505 | 3 | 100 |  |  |  | 36 | if ( $class->isa('Jifty::DBI::Collection') ) { | 
|  |  | 50 |  |  |  |  |  | 
| 506 | 2 |  |  |  |  | 6 | $reference = $class->record_class; | 
| 507 |  |  |  |  |  |  | } elsif ( $class->isa('Jifty::DBI::Record') ) { | 
| 508 | 1 |  |  |  |  | 2 | $reference = $class; | 
| 509 |  |  |  |  |  |  | } | 
| 510 |  |  |  |  |  |  |  | 
| 511 | 3 |  |  |  |  | 1471 | my $only_cols = $prefetch_related{$alias}{columns}; | 
| 512 |  |  |  |  |  |  |  | 
| 513 | 3 |  |  |  |  | 12 | push @cols, $self->_qualified_record_columns( $alias => $reference, $only_cols ); | 
| 514 |  |  |  |  |  |  | } | 
| 515 | 246 |  |  |  |  | 1708 | return CORE::join( ', ', @cols ); | 
| 516 |  |  |  |  |  |  | } | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | =head2 class_and_column_for_alias | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | Takes the alias you've assigned to a prefetched related | 
| 521 |  |  |  |  |  |  | object. Returns the class of the column we've declared that alias | 
| 522 |  |  |  |  |  |  | prefetches. | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | =cut | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | sub class_and_column_for_alias { | 
| 527 | 10 |  |  | 10 | 1 | 14 | my $self     = shift; | 
| 528 | 10 |  |  |  |  | 15 | my $alias    = shift; | 
| 529 | 10 | 50 |  |  |  | 11 | my %prefetch = %{ $self->prefetch_related || {} }; | 
|  | 10 |  |  |  |  | 32 |  | 
| 530 | 10 |  |  |  |  | 72 | my $related  = $prefetch{$alias}; | 
| 531 | 10 | 50 |  |  |  | 19 | return unless $related; | 
| 532 |  |  |  |  |  |  |  | 
| 533 | 10 |  |  |  |  | 40 | return $related->{class}, $related->{name}; | 
| 534 |  |  |  |  |  |  | } | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | sub _qualified_record_columns { | 
| 537 | 246 |  |  | 246 |  | 503 | my $self  = shift; | 
| 538 | 246 |  |  |  |  | 320 | my $alias = shift; | 
| 539 | 246 |  |  |  |  | 1355 | my $item  = shift; | 
| 540 | 246 |  |  |  |  | 321 | my $only_cols = shift; | 
| 541 | 246 |  | 66 |  |  | 1053 | my @columns = map { $_->name } grep { !$_->virtual && !$_->computed } $item->columns; | 
|  | 998 |  |  |  |  | 7685 |  | 
|  | 1016 |  |  |  |  | 13743 |  | 
| 542 | 246 | 50 |  |  |  | 2242 | if ($only_cols) { | 
| 543 | 0 |  |  |  |  | 0 | my %wanted = map { +($_ => 1) } @{ $only_cols }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 544 | 0 |  |  |  |  | 0 | @columns = grep { $wanted{$_} } @columns; | 
|  | 0 |  |  |  |  | 0 |  | 
| 545 |  |  |  |  |  |  | } | 
| 546 | 246 |  |  |  |  | 467 | return map {$alias ."." . $_ ." as ". $alias ."_". $_} @columns | 
|  | 998 |  |  |  |  | 3181 |  | 
| 547 |  |  |  |  |  |  | } | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | =head2 prefetch PARAMHASH | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | Prefetches properties of a related table, in the same query.  Possible | 
| 552 |  |  |  |  |  |  | keys in the paramhash are: | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | =over | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | =item name | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | This argument is required; it specifies the name of the collection or | 
| 559 |  |  |  |  |  |  | record that is to be prefetched.  If the name matches a column with a | 
| 560 |  |  |  |  |  |  | C relationship, the other arguments can be inferred, and | 
| 561 |  |  |  |  |  |  | this is the only parameter which needs to be passed. | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | It is possible to pass values for C which are not real columns | 
| 564 |  |  |  |  |  |  | in the model; these, while they won't be accessible by calling | 
| 565 |  |  |  |  |  |  | C<< $record-> I >> on records in this collection, will | 
| 566 |  |  |  |  |  |  | still be accessible by calling C<< $record->prefetched( I ) >>. | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | =item reference | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | Specifies the series of column names to traverse to extract the | 
| 571 |  |  |  |  |  |  | information.  For instance, if groups referred to multiple users, and | 
| 572 |  |  |  |  |  |  | users referred to multiple phone numbers, then providing | 
| 573 |  |  |  |  |  |  | C would do the two necessary joins to produce a phone | 
| 574 |  |  |  |  |  |  | collection for all users in each group. | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | This option defaults to the name, and is irrelevant if an C is | 
| 577 |  |  |  |  |  |  | provided. | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | =item alias | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | Specifies an alias which has already been joined to this collection as | 
| 582 |  |  |  |  |  |  | the source of the prefetched data.  C will also need to be | 
| 583 |  |  |  |  |  |  | specified. | 
| 584 |  |  |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | =item class | 
| 586 |  |  |  |  |  |  |  | 
| 587 |  |  |  |  |  |  | Specifies the class of the data to preload.  This is only necessary if | 
| 588 |  |  |  |  |  |  | C is provided, and C is not the name of a column which | 
| 589 |  |  |  |  |  |  | provides C information. | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | =back | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | For backwards compatibility, C can instead be called with | 
| 594 |  |  |  |  |  |  | C and C as its two arguments, instead of a paramhash. | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | =cut | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | sub prefetch { | 
| 599 | 3 |  |  | 3 | 1 | 35 | my $self = shift; | 
| 600 |  |  |  |  |  |  |  | 
| 601 |  |  |  |  |  |  | # Back-compat | 
| 602 | 3 | 100 | 66 |  |  | 30 | if ( @_ and $self->{joins}{ $_[0] } ) { | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | # First argument appears to be an alias | 
| 605 | 1 |  |  |  |  | 11 | @_ = ( alias => $_[0], name => $_[1] ); | 
| 606 |  |  |  |  |  |  | } | 
| 607 |  |  |  |  |  |  |  | 
| 608 | 3 |  |  |  |  | 19 | my %args = ( | 
| 609 |  |  |  |  |  |  | alias     => undef, | 
| 610 |  |  |  |  |  |  | name      => undef, | 
| 611 |  |  |  |  |  |  | class     => undef, | 
| 612 |  |  |  |  |  |  | reference => undef, | 
| 613 |  |  |  |  |  |  | columns   => undef, | 
| 614 |  |  |  |  |  |  | @_, | 
| 615 |  |  |  |  |  |  | ); | 
| 616 |  |  |  |  |  |  |  | 
| 617 | 3 | 50 |  |  |  | 10 | die "Must at least provide name to prefetch" | 
| 618 |  |  |  |  |  |  | unless $args{name}; | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | # Reference defaults to name | 
| 621 | 3 |  | 33 |  |  | 14 | $args{reference} ||= $args{name}; | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | # If we don't have an alias, do the join | 
| 624 | 3 | 100 |  |  |  | 11 | if ( not $args{alias} ) { | 
| 625 | 2 |  |  |  |  | 23 | my ( $class, @columns ) | 
| 626 |  |  |  |  |  |  | = $self->find_class( split /\./, $args{reference} ); | 
| 627 | 2 |  |  |  |  | 8 | $args{class} = ref $class; | 
| 628 | 2 |  |  |  |  | 19 | ( $args{alias} ) = $self->resolve_join(@columns); | 
| 629 |  |  |  |  |  |  | } | 
| 630 |  |  |  |  |  |  |  | 
| 631 | 3 | 100 |  |  |  | 11 | if ( not $args{class} ) { | 
| 632 |  |  |  |  |  |  |  | 
| 633 |  |  |  |  |  |  | # Check the column | 
| 634 | 1 |  |  |  |  | 3 | my $column = $self->record_class->column( $args{name} ); | 
| 635 | 1 | 50 |  |  |  | 19 | $args{class} = $column->refers_to if $column; | 
| 636 |  |  |  |  |  |  |  | 
| 637 | 1 | 50 |  |  |  | 10 | die "Don't know class" unless $args{class}; | 
| 638 |  |  |  |  |  |  | } | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | # Check that the class is a Jifty::DBI::Record or Jifty::DBI::Collection | 
| 641 | 3 | 50 | 66 |  |  | 41 | unless ( UNIVERSAL::isa( $args{class}, "Jifty::DBI::Record" ) | 
| 642 |  |  |  |  |  |  | or UNIVERSAL::isa( $args{class}, "Jifty::DBI::Collection" ) ) | 
| 643 |  |  |  |  |  |  | { | 
| 644 | 0 |  |  |  |  | 0 | warn | 
| 645 |  |  |  |  |  |  | "Class ($args{class}) isn't a Jifty::DBI::Record or Jifty::DBI::Collection"; | 
| 646 | 0 |  |  |  |  | 0 | return undef; | 
| 647 |  |  |  |  |  |  | } | 
| 648 |  |  |  |  |  |  |  | 
| 649 | 3 | 50 |  |  |  | 20 | $self->prefetch_related( {} ) unless $self->prefetch_related; | 
| 650 | 3 |  |  |  |  | 46 | $self->prefetch_related->{ $args{alias} } = {}; | 
| 651 |  |  |  |  |  |  | $self->prefetch_related->{ $args{alias} }{$_} = $args{$_} | 
| 652 | 3 |  |  |  |  | 27 | for qw/alias class name columns/; | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | # Return the alias, in case we made it | 
| 655 | 3 |  |  |  |  | 71 | return $args{alias}; | 
| 656 |  |  |  |  |  |  | } | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | =head2 find_column NAMES | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | Tales a chained list of column names, where all but the last element | 
| 661 |  |  |  |  |  |  | is the name of a column on the previous class which refers to the next | 
| 662 |  |  |  |  |  |  | collection or record.  Returns a list of L objects | 
| 663 |  |  |  |  |  |  | for the list. | 
| 664 |  |  |  |  |  |  |  | 
| 665 |  |  |  |  |  |  | =cut | 
| 666 |  |  |  |  |  |  |  | 
| 667 |  |  |  |  |  |  | sub find_column { | 
| 668 | 0 |  |  | 0 | 1 | 0 | my $self  = shift; | 
| 669 | 0 |  |  |  |  | 0 | my @names = @_; | 
| 670 |  |  |  |  |  |  |  | 
| 671 | 0 |  |  |  |  | 0 | my $last = pop @names; | 
| 672 | 0 |  |  |  |  | 0 | my ( $class, @columns ) = $self->find_class(@names); | 
| 673 | 0 | 0 |  |  |  | 0 | $class = $class->record_class | 
| 674 |  |  |  |  |  |  | if UNIVERSAL::isa( $class, "Jifty::DBI::Collection" ); | 
| 675 | 0 |  |  |  |  | 0 | my $column = $class->column($last); | 
| 676 | 0 | 0 |  |  |  | 0 | die "$class has no column '$last'" unless $column; | 
| 677 | 0 |  |  |  |  | 0 | return @columns, $column; | 
| 678 |  |  |  |  |  |  | } | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | =head2 find_class NAMES | 
| 681 |  |  |  |  |  |  |  | 
| 682 |  |  |  |  |  |  | Tales a chained list of column names, where each element is the name | 
| 683 |  |  |  |  |  |  | of a column on the previous class which refers to the next collection | 
| 684 |  |  |  |  |  |  | or record.  Returns an instance of the ending class, followed by the | 
| 685 |  |  |  |  |  |  | list of L objects traversed to get there. | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | =cut | 
| 688 |  |  |  |  |  |  |  | 
| 689 |  |  |  |  |  |  | sub find_class { | 
| 690 | 2 |  |  | 2 | 1 | 5 | my $self  = shift; | 
| 691 | 2 |  |  |  |  | 5 | my @names = @_; | 
| 692 |  |  |  |  |  |  |  | 
| 693 | 2 |  |  |  |  | 4 | my @res; | 
| 694 | 2 |  |  |  |  | 2 | my $object = $self; | 
| 695 | 2 |  |  |  |  | 4 | my $itemclass = $self->record_class; | 
| 696 | 2 |  |  |  |  | 8 | while ( my $name = shift @names ) { | 
| 697 | 2 |  |  |  |  | 10 | my $column = $itemclass->column($name); | 
| 698 | 2 | 50 |  |  |  | 7 | die "$itemclass has no column '$name'" unless $column; | 
| 699 |  |  |  |  |  |  |  | 
| 700 | 2 |  |  |  |  | 4 | push @res, $column; | 
| 701 |  |  |  |  |  |  |  | 
| 702 | 2 |  |  |  |  | 7 | my $classname = $column->refers_to; | 
| 703 | 2 | 50 |  |  |  | 12 | unless ($classname) { | 
| 704 | 0 |  |  |  |  | 0 | die "column '$name' of $itemclass is not a reference"; | 
| 705 |  |  |  |  |  |  | } | 
| 706 |  |  |  |  |  |  |  | 
| 707 | 2 | 100 |  |  |  | 20 | if ( UNIVERSAL::isa( $classname, 'Jifty::DBI::Collection' ) ) { | 
|  |  | 50 |  |  |  |  |  | 
| 708 | 1 |  |  |  |  | 5 | $object = $classname->new( $self->_new_collection_args ); | 
| 709 | 1 |  |  |  |  | 3 | $itemclass = $object->record_class; | 
| 710 |  |  |  |  |  |  | } elsif ( UNIVERSAL::isa( $classname, 'Jifty::DBI::Record' ) ) { | 
| 711 | 1 |  |  |  |  | 5 | $object = $classname->new( $self->_new_record_args ); | 
| 712 | 1 |  |  |  |  | 7 | $itemclass = $classname; | 
| 713 |  |  |  |  |  |  | } else { | 
| 714 | 0 |  |  |  |  | 0 | die | 
| 715 |  |  |  |  |  |  | "Column '$name' refers to '$classname' which is not record or collection"; | 
| 716 |  |  |  |  |  |  | } | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  |  | 
| 719 | 2 |  |  |  |  | 281 | return $object, @res; | 
| 720 |  |  |  |  |  |  | } | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | =head2 resolve_join COLUMNS | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | Takes a chained list of L objects, and performs | 
| 725 |  |  |  |  |  |  | the requisite joins to join all of them.  Returns the alias of the | 
| 726 |  |  |  |  |  |  | last join. | 
| 727 |  |  |  |  |  |  |  | 
| 728 |  |  |  |  |  |  | =cut | 
| 729 |  |  |  |  |  |  |  | 
| 730 |  |  |  |  |  |  | sub resolve_join { | 
| 731 | 2 |  |  | 2 | 1 | 5 | my $self  = shift; | 
| 732 | 2 |  |  |  |  | 4 | my @chain = @_; | 
| 733 |  |  |  |  |  |  |  | 
| 734 | 2 |  |  |  |  | 4 | my $last_alias = 'main'; | 
| 735 |  |  |  |  |  |  |  | 
| 736 | 2 |  |  |  |  | 7 | foreach my $column (@chain) { | 
| 737 | 2 |  |  |  |  | 10 | my $name = $column->name; | 
| 738 |  |  |  |  |  |  |  | 
| 739 | 2 |  |  |  |  | 17 | my $classname = $column->refers_to; | 
| 740 | 2 | 50 |  |  |  | 11 | unless ($classname) { | 
| 741 | 0 |  |  |  |  | 0 | die "column '$name' of is not a reference"; | 
| 742 |  |  |  |  |  |  | } | 
| 743 |  |  |  |  |  |  |  | 
| 744 | 2 | 100 |  |  |  | 578 | if ( UNIVERSAL::isa( $classname, 'Jifty::DBI::Collection' ) ) { | 
|  |  | 50 |  |  |  |  |  | 
| 745 | 1 |  |  |  |  | 4 | my $right_alias = $self->new_alias($classname->record_class); | 
| 746 | 1 |  | 50 |  |  | 6 | $self->join( | 
| 747 |  |  |  |  |  |  | type        => 'left', | 
| 748 |  |  |  |  |  |  | alias1      => $last_alias, | 
| 749 |  |  |  |  |  |  | column1     => 'id', | 
| 750 |  |  |  |  |  |  | alias2      => $right_alias, | 
| 751 |  |  |  |  |  |  | column2     => $column->by || 'id', | 
| 752 |  |  |  |  |  |  | is_distinct => 1, | 
| 753 |  |  |  |  |  |  | ); | 
| 754 | 1 |  |  |  |  | 3 | $last_alias = $right_alias; | 
| 755 |  |  |  |  |  |  | } elsif ( UNIVERSAL::isa( $classname, 'Jifty::DBI::Record' ) ) { | 
| 756 | 1 |  |  |  |  | 12 | my $right_alias = $self->new_alias($classname); | 
| 757 | 1 |  | 50 |  |  | 8 | $self->join( | 
| 758 |  |  |  |  |  |  | type        => 'left', | 
| 759 |  |  |  |  |  |  | alias1      => $last_alias, | 
| 760 |  |  |  |  |  |  | column1     => $name, | 
| 761 |  |  |  |  |  |  | alias2      => $right_alias, | 
| 762 |  |  |  |  |  |  | column2     => $column->by || 'id', | 
| 763 |  |  |  |  |  |  | is_distinct => 1, | 
| 764 |  |  |  |  |  |  | ); | 
| 765 | 1 |  |  |  |  | 4 | $last_alias = $right_alias; | 
| 766 |  |  |  |  |  |  | } else { | 
| 767 | 0 |  |  |  |  | 0 | die | 
| 768 |  |  |  |  |  |  | "Column '$name' refers to '$classname' which is not record or collection"; | 
| 769 |  |  |  |  |  |  | } | 
| 770 |  |  |  |  |  |  | } | 
| 771 | 2 |  |  |  |  | 15 | return $last_alias; | 
| 772 |  |  |  |  |  |  | } | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | =head2 distinct_required | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | Returns true if Jifty::DBI expects that this result set will end up | 
| 777 |  |  |  |  |  |  | with repeated rows and should be "condensed" down to a single row for | 
| 778 |  |  |  |  |  |  | each unique primary key. | 
| 779 |  |  |  |  |  |  |  | 
| 780 |  |  |  |  |  |  | Out of the box, this method returns true if you've joined to another table. | 
| 781 |  |  |  |  |  |  | To add additional logic, feel free to override this method in your subclass. | 
| 782 |  |  |  |  |  |  |  | 
| 783 |  |  |  |  |  |  | XXX TODO: it should be possible to create a better heuristic than the simple | 
| 784 |  |  |  |  |  |  | "is it joined?" question we're asking now. Something along the lines of "are we | 
| 785 |  |  |  |  |  |  | joining this table to something that is not the other table's primary key" | 
| 786 |  |  |  |  |  |  |  | 
| 787 |  |  |  |  |  |  | =cut | 
| 788 |  |  |  |  |  |  |  | 
| 789 |  |  |  |  |  |  | sub distinct_required { | 
| 790 | 324 |  |  | 324 | 1 | 562 | my $self = shift; | 
| 791 | 324 | 100 |  |  |  | 882 | return ( $self->_is_joined ? !$self->_is_distinctly_joined : 0 ); | 
| 792 |  |  |  |  |  |  | } | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | =head2 build_select_count_query | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | Builds a SELECT statement to find the number of rows this collection | 
| 797 |  |  |  |  |  |  | would find. | 
| 798 |  |  |  |  |  |  |  | 
| 799 |  |  |  |  |  |  | =cut | 
| 800 |  |  |  |  |  |  |  | 
| 801 |  |  |  |  |  |  | sub build_select_count_query { | 
| 802 | 78 |  |  | 78 | 1 | 329 | my $self = shift; | 
| 803 |  |  |  |  |  |  |  | 
| 804 | 78 | 50 |  |  |  | 254 | return "" if $self->derived; | 
| 805 |  |  |  |  |  |  |  | 
| 806 | 78 |  |  |  |  | 616 | my $query_string = $self->_build_joins . " "; | 
| 807 |  |  |  |  |  |  |  | 
| 808 | 78 | 50 |  |  |  | 269 | if ( $self->_is_limited ) { | 
| 809 | 78 |  |  |  |  | 703 | $query_string .= $self->_where_clause . " "; | 
| 810 |  |  |  |  |  |  | } | 
| 811 |  |  |  |  |  |  |  | 
| 812 |  |  |  |  |  |  | # DISTINCT query only required for multi-table selects | 
| 813 | 78 | 100 | 100 |  |  | 576 | if ( $self->distinct_required or $self->prefetch_related ) { | 
| 814 | 11 |  |  |  |  | 50 | $query_string = $self->_handle->distinct_count( \$query_string ); | 
| 815 |  |  |  |  |  |  | } else { | 
| 816 | 67 |  |  |  |  | 581 | $query_string = "SELECT count(main.id) FROM " . $query_string; | 
| 817 |  |  |  |  |  |  | } | 
| 818 |  |  |  |  |  |  |  | 
| 819 | 78 |  |  |  |  | 221 | return ($query_string); | 
| 820 |  |  |  |  |  |  | } | 
| 821 |  |  |  |  |  |  |  | 
| 822 |  |  |  |  |  |  | =head2 do_search | 
| 823 |  |  |  |  |  |  |  | 
| 824 |  |  |  |  |  |  | C usually does searches "lazily". That is, it | 
| 825 |  |  |  |  |  |  | does a C | 
| 826 |  |  |  |  |  |  | for results that would need one or the other.  Sometimes, you need to | 
| 827 |  |  |  |  |  |  | display a count of results found before you iterate over a collection, | 
| 828 |  |  |  |  |  |  | but you know you're about to do that too. To save a bit of wear and tear | 
| 829 |  |  |  |  |  |  | on your database, call C before that C. | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | =cut | 
| 832 |  |  |  |  |  |  |  | 
| 833 |  |  |  |  |  |  | sub do_search { | 
| 834 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 835 | 0 | 0 |  |  |  | 0 | return if $self->derived; | 
| 836 | 0 | 0 |  |  |  | 0 | $self->_do_search() if $self->{'must_redo_search'}; | 
| 837 |  |  |  |  |  |  |  | 
| 838 |  |  |  |  |  |  | } | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | =head2 next | 
| 841 |  |  |  |  |  |  |  | 
| 842 |  |  |  |  |  |  | Returns the next row from the set as an object of the type defined by | 
| 843 |  |  |  |  |  |  | sub new_item.  When the complete set has been iterated through, | 
| 844 |  |  |  |  |  |  | returns undef and resets the search such that the following call to | 
| 845 |  |  |  |  |  |  | L will start over with the first item retrieved from the | 
| 846 |  |  |  |  |  |  | database. | 
| 847 |  |  |  |  |  |  |  | 
| 848 |  |  |  |  |  |  | You may also call this method via the built-in iterator syntax. | 
| 849 |  |  |  |  |  |  | The two lines below are equivalent: | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  | while ($_ = $collection->next) { ... } | 
| 852 |  |  |  |  |  |  |  | 
| 853 |  |  |  |  |  |  | while (<$collection>) { ... } | 
| 854 |  |  |  |  |  |  |  | 
| 855 |  |  |  |  |  |  | =cut | 
| 856 |  |  |  |  |  |  |  | 
| 857 |  |  |  |  |  |  | sub next { | 
| 858 | 83 |  |  | 83 | 1 | 3967 | my $self = shift; | 
| 859 |  |  |  |  |  |  |  | 
| 860 | 83 |  |  |  |  | 297 | my $item = $self->peek; | 
| 861 |  |  |  |  |  |  |  | 
| 862 | 83 | 100 |  |  |  | 357 | if ( $self->{'itemscount'} < $self->_record_count ) { | 
| 863 | 65 |  |  |  |  | 147 | $self->{'itemscount'}++; | 
| 864 |  |  |  |  |  |  | } else {    #we've gone through the whole list. reset the count. | 
| 865 | 18 |  |  |  |  | 56 | $self->goto_first_item(); | 
| 866 |  |  |  |  |  |  | } | 
| 867 |  |  |  |  |  |  |  | 
| 868 | 83 |  |  |  |  | 2483 | return ($item); | 
| 869 |  |  |  |  |  |  | } | 
| 870 |  |  |  |  |  |  |  | 
| 871 |  |  |  |  |  |  | =head2 peek | 
| 872 |  |  |  |  |  |  |  | 
| 873 |  |  |  |  |  |  | Exactly the same as next, only it doesn't move the iterator. | 
| 874 |  |  |  |  |  |  |  | 
| 875 |  |  |  |  |  |  | =cut | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | sub peek { | 
| 878 | 91 |  |  | 91 | 1 | 140 | my $self = shift; | 
| 879 |  |  |  |  |  |  |  | 
| 880 | 91 | 100 |  |  |  | 343 | return (undef) unless ( $self->_is_limited ); | 
| 881 |  |  |  |  |  |  |  | 
| 882 | 83 | 100 |  |  |  | 881 | $self->_do_search() if $self->{'must_redo_search'}; | 
| 883 |  |  |  |  |  |  |  | 
| 884 | 83 | 100 |  |  |  | 303 | if ( $self->{'itemscount'} < $self->_record_count ) | 
| 885 |  |  |  |  |  |  | {    #return the next item | 
| 886 | 67 |  |  |  |  | 409 | my $item = ( $self->{'items'}[ $self->{'itemscount'} ] ); | 
| 887 | 67 |  |  |  |  | 175 | return ($item); | 
| 888 |  |  |  |  |  |  | } else {    #no more items! | 
| 889 | 16 |  |  |  |  | 41 | return (undef); | 
| 890 |  |  |  |  |  |  | } | 
| 891 |  |  |  |  |  |  | } | 
| 892 |  |  |  |  |  |  |  | 
| 893 |  |  |  |  |  |  | =head2 goto_first_item | 
| 894 |  |  |  |  |  |  |  | 
| 895 |  |  |  |  |  |  | Starts the recordset counter over from the first item. The next time | 
| 896 |  |  |  |  |  |  | you call L, you'll get the first item returned by the database, | 
| 897 |  |  |  |  |  |  | as if you'd just started iterating through the result set. | 
| 898 |  |  |  |  |  |  |  | 
| 899 |  |  |  |  |  |  | =cut | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | sub goto_first_item { | 
| 902 | 59 |  |  | 59 | 1 | 608 | my $self = shift; | 
| 903 | 59 |  |  |  |  | 217 | $self->goto_item(0); | 
| 904 |  |  |  |  |  |  | } | 
| 905 |  |  |  |  |  |  |  | 
| 906 |  |  |  |  |  |  | =head2 goto_item | 
| 907 |  |  |  |  |  |  |  | 
| 908 |  |  |  |  |  |  | Takes an integer, n.  Sets the record counter to n. the next time you | 
| 909 |  |  |  |  |  |  | call L, you'll get the nth item. | 
| 910 |  |  |  |  |  |  |  | 
| 911 |  |  |  |  |  |  | =cut | 
| 912 |  |  |  |  |  |  |  | 
| 913 |  |  |  |  |  |  | sub goto_item { | 
| 914 | 65 |  |  | 65 | 1 | 139 | my $self = shift; | 
| 915 | 65 |  |  |  |  | 98 | my $item = shift; | 
| 916 | 65 |  |  |  |  | 210 | $self->{'itemscount'} = $item; | 
| 917 |  |  |  |  |  |  | } | 
| 918 |  |  |  |  |  |  |  | 
| 919 |  |  |  |  |  |  | =head2 first | 
| 920 |  |  |  |  |  |  |  | 
| 921 |  |  |  |  |  |  | Returns the first item | 
| 922 |  |  |  |  |  |  |  | 
| 923 |  |  |  |  |  |  | =cut | 
| 924 |  |  |  |  |  |  |  | 
| 925 |  |  |  |  |  |  | sub first { | 
| 926 | 35 |  |  | 35 | 1 | 1968 | my $self = shift; | 
| 927 | 35 |  |  |  |  | 148 | $self->goto_first_item(); | 
| 928 | 35 |  |  |  |  | 184 | return ( $self->next ); | 
| 929 |  |  |  |  |  |  | } | 
| 930 |  |  |  |  |  |  |  | 
| 931 |  |  |  |  |  |  | =head2 last | 
| 932 |  |  |  |  |  |  |  | 
| 933 |  |  |  |  |  |  | Returns the last item | 
| 934 |  |  |  |  |  |  |  | 
| 935 |  |  |  |  |  |  | =cut | 
| 936 |  |  |  |  |  |  |  | 
| 937 |  |  |  |  |  |  | sub last { | 
| 938 | 6 |  |  | 6 | 1 | 15 | my $self = shift; | 
| 939 | 6 |  |  |  |  | 22 | $self->goto_item( ( $self->count ) - 1 ); | 
| 940 | 6 |  |  |  |  | 23 | return ( $self->next ); | 
| 941 |  |  |  |  |  |  | } | 
| 942 |  |  |  |  |  |  |  | 
| 943 |  |  |  |  |  |  | =head2 distinct_column_values | 
| 944 |  |  |  |  |  |  |  | 
| 945 |  |  |  |  |  |  | Takes a column name and returns distinct values of the column. | 
| 946 |  |  |  |  |  |  | Only values in the current collection are returned. | 
| 947 |  |  |  |  |  |  |  | 
| 948 |  |  |  |  |  |  | Optional arguments are C and C to limit number of | 
| 949 |  |  |  |  |  |  | values returned and it makes sense to sort results. | 
| 950 |  |  |  |  |  |  |  | 
| 951 |  |  |  |  |  |  | $col->distinct_column_values('column'); | 
| 952 |  |  |  |  |  |  |  | 
| 953 |  |  |  |  |  |  | $col->distinct_column_values(column => 'column'); | 
| 954 |  |  |  |  |  |  |  | 
| 955 |  |  |  |  |  |  | $col->distinct_column_values('column', max => 10, sort => 'asc'); | 
| 956 |  |  |  |  |  |  |  | 
| 957 |  |  |  |  |  |  | =cut | 
| 958 |  |  |  |  |  |  |  | 
| 959 |  |  |  |  |  |  | sub distinct_column_values { | 
| 960 | 4 |  |  | 4 | 1 | 2570 | my $self = shift; | 
| 961 | 4 | 50 |  |  |  | 1292 | my %args = ( | 
| 962 |  |  |  |  |  |  | column => undef, | 
| 963 |  |  |  |  |  |  | sort   => undef, | 
| 964 |  |  |  |  |  |  | max    => undef, | 
| 965 |  |  |  |  |  |  | @_%2 ? (column => @_) : (@_) | 
| 966 |  |  |  |  |  |  | ); | 
| 967 |  |  |  |  |  |  |  | 
| 968 | 4 | 50 |  |  |  | 27 | return () if $self->derived; | 
| 969 |  |  |  |  |  |  |  | 
| 970 | 4 |  |  |  |  | 43 | my $query_string = $self->_build_joins; | 
| 971 | 4 | 50 |  |  |  | 15 | if ( $self->_is_limited ) { | 
| 972 | 0 |  |  |  |  | 0 | $query_string .= ' '. $self->_where_clause . " "; | 
| 973 |  |  |  |  |  |  | } | 
| 974 |  |  |  |  |  |  |  | 
| 975 | 4 |  |  |  |  | 28 | my $column = 'main.'. $args{'column'}; | 
| 976 | 4 |  |  |  |  | 10 | $query_string = 'SELECT DISTINCT '. $column .' FROM '. $query_string; | 
| 977 |  |  |  |  |  |  |  | 
| 978 | 4 | 100 |  |  |  | 26 | if ( $args{'sort'} ) { | 
| 979 | 3 | 100 |  |  |  | 17 | $query_string .= ' ORDER BY '. $column | 
| 980 |  |  |  |  |  |  | .' '. ($args{'sort'} =~ /^des/i ? 'DESC' : 'ASC'); | 
| 981 |  |  |  |  |  |  | } | 
| 982 |  |  |  |  |  |  |  | 
| 983 | 4 | 50 |  |  |  | 40 | my $sth  = $self->_handle->simple_query( $query_string ) or return; | 
| 984 | 4 |  |  |  |  | 6 | my $value; | 
| 985 | 4 | 50 |  |  |  | 30 | $sth->bind_col(1, \$value) or return; | 
| 986 | 4 |  |  |  |  | 6 | my @col; | 
| 987 | 4 | 100 |  |  |  | 9 | if ($args{max}) { | 
| 988 | 1 |  | 66 |  |  | 285 | push @col, $value while 0 < $args{max}-- && $sth->fetch; | 
| 989 |  |  |  |  |  |  | } else { | 
| 990 | 3 |  |  |  |  | 102 | push @col, $value while $sth->fetch; | 
| 991 |  |  |  |  |  |  | } | 
| 992 | 4 |  |  |  |  | 106 | return @col; | 
| 993 |  |  |  |  |  |  | } | 
| 994 |  |  |  |  |  |  |  | 
| 995 |  |  |  |  |  |  | =head2 items_array_ref | 
| 996 |  |  |  |  |  |  |  | 
| 997 |  |  |  |  |  |  | Return a reference to an array containing all objects found by this | 
| 998 |  |  |  |  |  |  | search. | 
| 999 |  |  |  |  |  |  |  | 
| 1000 |  |  |  |  |  |  | You may also call this method via the built-in array dereference syntax. | 
| 1001 |  |  |  |  |  |  | The two lines below are equivalent: | 
| 1002 |  |  |  |  |  |  |  | 
| 1003 |  |  |  |  |  |  | for (@{$collection->items_array_ref}) { ... } | 
| 1004 |  |  |  |  |  |  |  | 
| 1005 |  |  |  |  |  |  | for (@$collection) { ... } | 
| 1006 |  |  |  |  |  |  |  | 
| 1007 |  |  |  |  |  |  | =cut | 
| 1008 |  |  |  |  |  |  |  | 
| 1009 |  |  |  |  |  |  | sub items_array_ref { | 
| 1010 | 8 |  |  | 8 | 1 | 2167 | my $self = shift; | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 |  |  |  |  |  |  | # If we're not limited, return an empty array | 
| 1013 | 8 | 100 |  |  |  | 33 | return [] unless $self->_is_limited; | 
| 1014 |  |  |  |  |  |  |  | 
| 1015 |  |  |  |  |  |  | # Do a search if we need to. | 
| 1016 | 6 | 100 |  |  |  | 58 | $self->_do_search() if $self->{'must_redo_search'}; | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 |  |  |  |  |  |  | # If we've got any items in the array, return them.  Otherwise, | 
| 1019 |  |  |  |  |  |  | # return an empty array | 
| 1020 | 6 |  | 50 |  |  | 35 | return ( $self->{'items'} || [] ); | 
| 1021 |  |  |  |  |  |  | } | 
| 1022 |  |  |  |  |  |  |  | 
| 1023 |  |  |  |  |  |  | =head2 new_item | 
| 1024 |  |  |  |  |  |  |  | 
| 1025 |  |  |  |  |  |  | Should return a new object of the correct type for the current collection. | 
| 1026 |  |  |  |  |  |  | L method is used to determine class of the object. | 
| 1027 |  |  |  |  |  |  |  | 
| 1028 |  |  |  |  |  |  | Each record class at least once is loaded using require. This method is | 
| 1029 |  |  |  |  |  |  | called each time a record fetched so load attempts are cached to avoid | 
| 1030 |  |  |  |  |  |  | penalties. If you're sure that all record classes are loaded before | 
| 1031 |  |  |  |  |  |  | first use then you can override this method. | 
| 1032 |  |  |  |  |  |  |  | 
| 1033 |  |  |  |  |  |  | =cut | 
| 1034 |  |  |  |  |  |  |  | 
| 1035 |  |  |  |  |  |  | { my %cache = (); | 
| 1036 |  |  |  |  |  |  | sub new_item { | 
| 1037 | 185 |  |  | 185 | 1 | 564 | my $self  = shift; | 
| 1038 | 185 |  |  |  |  | 391 | my $class = $self->record_class(); | 
| 1039 |  |  |  |  |  |  |  | 
| 1040 | 185 | 50 |  |  |  | 425 | die "Jifty::DBI::Collection needs to be subclassed; override new_item\n" | 
| 1041 |  |  |  |  |  |  | unless $class; | 
| 1042 |  |  |  |  |  |  |  | 
| 1043 | 185 | 100 |  |  |  | 465 | unless ( exists $cache{$class} ) { | 
| 1044 | 9 |  |  |  |  | 239 | $class->require; | 
| 1045 | 9 |  |  |  |  | 203 | $cache{$class} = undef; | 
| 1046 |  |  |  |  |  |  | } | 
| 1047 | 185 |  |  |  |  | 515 | return $class->new( $self->_new_record_args ); | 
| 1048 |  |  |  |  |  |  | } } | 
| 1049 |  |  |  |  |  |  |  | 
| 1050 |  |  |  |  |  |  | =head2 record_class | 
| 1051 |  |  |  |  |  |  |  | 
| 1052 |  |  |  |  |  |  | Returns the record class which this is a collection of; override this | 
| 1053 |  |  |  |  |  |  | to subclass.  Or, pass it the name of a class as an argument after | 
| 1054 |  |  |  |  |  |  | creating a C object to create an 'anonymous' | 
| 1055 |  |  |  |  |  |  | collection class. | 
| 1056 |  |  |  |  |  |  |  | 
| 1057 |  |  |  |  |  |  | If you haven't specified a record class, this returns a best guess at | 
| 1058 |  |  |  |  |  |  | the name of the record class for this collection. | 
| 1059 |  |  |  |  |  |  |  | 
| 1060 |  |  |  |  |  |  | It uses a simple heuristic to determine the record class name -- It | 
| 1061 |  |  |  |  |  |  | chops "Collection" or "s" off its own name. If you want to name your | 
| 1062 |  |  |  |  |  |  | records and collections differently, go right ahead, but don't say we | 
| 1063 |  |  |  |  |  |  | didn't warn you. | 
| 1064 |  |  |  |  |  |  |  | 
| 1065 |  |  |  |  |  |  | =cut | 
| 1066 |  |  |  |  |  |  |  | 
| 1067 |  |  |  |  |  |  | sub record_class { | 
| 1068 | 948 |  |  | 948 | 1 | 1425 | my $self = shift; | 
| 1069 | 948 | 100 | 100 |  |  | 6321 | if (@_) { | 
|  |  | 100 |  |  |  |  |  | 
| 1070 | 2 | 50 |  |  |  | 12 | $self->{record_class} = shift if (@_); | 
| 1071 | 2 | 50 |  |  |  | 9 | $self->{record_class} = ref $self->{record_class} | 
| 1072 |  |  |  |  |  |  | if ref $self->{record_class}; | 
| 1073 |  |  |  |  |  |  | } elsif ( not ref $self or not $self->{record_class} ) { | 
| 1074 | 231 |  | 66 |  |  | 753 | my $class = ref($self) || $self; | 
| 1075 | 231 | 50 |  |  |  | 1701 | $class =~ s/(? | 
| 1076 |  |  |  |  |  |  | || die "Can't guess record class from $class"; | 
| 1077 | 231 | 100 |  |  |  | 553 | return $class unless ref $self; | 
| 1078 | 228 |  |  |  |  | 577 | $self->{record_class} = $class; | 
| 1079 |  |  |  |  |  |  | } | 
| 1080 | 945 |  |  |  |  | 7431 | return $self->{record_class}; | 
| 1081 |  |  |  |  |  |  | } | 
| 1082 |  |  |  |  |  |  |  | 
| 1083 |  |  |  |  |  |  | =head2 redo_search | 
| 1084 |  |  |  |  |  |  |  | 
| 1085 |  |  |  |  |  |  | Takes no arguments.  Tells Jifty::DBI::Collection that the next time | 
| 1086 |  |  |  |  |  |  | it is asked for a record, it should re-execute the query. | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 |  |  |  |  |  |  | =cut | 
| 1089 |  |  |  |  |  |  |  | 
| 1090 |  |  |  |  |  |  | sub redo_search { | 
| 1091 | 597 |  |  | 597 | 1 | 5850 | my $self = shift; | 
| 1092 | 597 |  |  |  |  | 1216 | $self->{'must_redo_search'} = 1; | 
| 1093 | 597 |  |  |  |  | 3429 | delete $self->{$_} for qw(items raw_rows count_all); | 
| 1094 | 597 |  |  |  |  | 1927 | $self->{'itemscount'} = 0; | 
| 1095 |  |  |  |  |  |  | } | 
| 1096 |  |  |  |  |  |  |  | 
| 1097 |  |  |  |  |  |  | =head2 unlimit | 
| 1098 |  |  |  |  |  |  |  | 
| 1099 |  |  |  |  |  |  | Unlimit clears all restrictions on this collection and resets | 
| 1100 |  |  |  |  |  |  | it to a "default" pristine state. Note, in particular, that | 
| 1101 |  |  |  |  |  |  | this means C will erase ordering and grouping | 
| 1102 |  |  |  |  |  |  | metadata.  To find all rows without resetting this metadata, | 
| 1103 |  |  |  |  |  |  | use the C method. | 
| 1104 |  |  |  |  |  |  |  | 
| 1105 |  |  |  |  |  |  | =cut | 
| 1106 |  |  |  |  |  |  |  | 
| 1107 |  |  |  |  |  |  | sub unlimit { | 
| 1108 | 7 |  |  | 7 | 1 | 5238 | my $self = shift; | 
| 1109 |  |  |  |  |  |  |  | 
| 1110 | 7 |  |  |  |  | 30 | $self->clean_slate(); | 
| 1111 | 7 |  |  |  |  | 145 | $self->_is_limited(-1); | 
| 1112 |  |  |  |  |  |  | } | 
| 1113 |  |  |  |  |  |  |  | 
| 1114 |  |  |  |  |  |  | =head2 find_all_rows | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | C instructs this collection class to return all rows in | 
| 1117 |  |  |  |  |  |  | the table. (It removes the WHERE clause from your query). | 
| 1118 |  |  |  |  |  |  |  | 
| 1119 |  |  |  |  |  |  | =cut | 
| 1120 |  |  |  |  |  |  |  | 
| 1121 |  |  |  |  |  |  | sub find_all_rows { | 
| 1122 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 1123 | 0 |  |  |  |  | 0 | $self->_is_limited(-1); | 
| 1124 |  |  |  |  |  |  | } | 
| 1125 |  |  |  |  |  |  |  | 
| 1126 |  |  |  |  |  |  | =head2 limit | 
| 1127 |  |  |  |  |  |  |  | 
| 1128 |  |  |  |  |  |  | Takes a hash of parameters with the following keys: | 
| 1129 |  |  |  |  |  |  |  | 
| 1130 |  |  |  |  |  |  | =over 4 | 
| 1131 |  |  |  |  |  |  |  | 
| 1132 |  |  |  |  |  |  | =item table | 
| 1133 |  |  |  |  |  |  |  | 
| 1134 |  |  |  |  |  |  | Can be set to something different than this table if a join is | 
| 1135 |  |  |  |  |  |  | wanted (that means we can't do recursive joins as for now). | 
| 1136 |  |  |  |  |  |  |  | 
| 1137 |  |  |  |  |  |  | =item alias | 
| 1138 |  |  |  |  |  |  |  | 
| 1139 |  |  |  |  |  |  | Unless alias is set, the join criteria will be taken from EXT_LINKcolumn | 
| 1140 |  |  |  |  |  |  | and INT_LINKcolumn and added to the criteria.  If alias is set, new | 
| 1141 |  |  |  |  |  |  | criteria about the foreign table will be added. | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 |  |  |  |  |  |  | =item column | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 |  |  |  |  |  |  | Column to be checked against. | 
| 1146 |  |  |  |  |  |  |  | 
| 1147 |  |  |  |  |  |  | =item value | 
| 1148 |  |  |  |  |  |  |  | 
| 1149 |  |  |  |  |  |  | Should always be set and will always be quoted.  If the value is a | 
| 1150 |  |  |  |  |  |  | subclass of Jifty::DBI::Object, the value will be interpreted to be | 
| 1151 |  |  |  |  |  |  | the object's id. | 
| 1152 |  |  |  |  |  |  |  | 
| 1153 |  |  |  |  |  |  | =item operator | 
| 1154 |  |  |  |  |  |  |  | 
| 1155 |  |  |  |  |  |  | operator is the SQL operator to use for this phrase.  Possible choices include: | 
| 1156 |  |  |  |  |  |  |  | 
| 1157 |  |  |  |  |  |  | =over 4 | 
| 1158 |  |  |  |  |  |  |  | 
| 1159 |  |  |  |  |  |  | =item "=" | 
| 1160 |  |  |  |  |  |  |  | 
| 1161 |  |  |  |  |  |  | =item "!=" | 
| 1162 |  |  |  |  |  |  |  | 
| 1163 |  |  |  |  |  |  | Any other standard SQL comparison operators that your underlying | 
| 1164 |  |  |  |  |  |  | database supports are also valid. | 
| 1165 |  |  |  |  |  |  |  | 
| 1166 |  |  |  |  |  |  | =item "LIKE" | 
| 1167 |  |  |  |  |  |  |  | 
| 1168 |  |  |  |  |  |  | =item "NOT LIKE" | 
| 1169 |  |  |  |  |  |  |  | 
| 1170 |  |  |  |  |  |  | =item "MATCHES" | 
| 1171 |  |  |  |  |  |  |  | 
| 1172 |  |  |  |  |  |  | MATCHES is like LIKE, except it surrounds the value with % signs. | 
| 1173 |  |  |  |  |  |  |  | 
| 1174 |  |  |  |  |  |  | =item "starts_with" | 
| 1175 |  |  |  |  |  |  |  | 
| 1176 |  |  |  |  |  |  | starts_with is like LIKE, except it only appends a % at the end of the string | 
| 1177 |  |  |  |  |  |  |  | 
| 1178 |  |  |  |  |  |  | =item "ends_with" | 
| 1179 |  |  |  |  |  |  |  | 
| 1180 |  |  |  |  |  |  | ends_with is like LIKE, except it prepends a % to the beginning of the string | 
| 1181 |  |  |  |  |  |  |  | 
| 1182 |  |  |  |  |  |  | =item "IN" | 
| 1183 |  |  |  |  |  |  |  | 
| 1184 |  |  |  |  |  |  | IN matches a column within a set of values.  The value specified in the limit | 
| 1185 |  |  |  |  |  |  | should be an array reference of values. | 
| 1186 |  |  |  |  |  |  |  | 
| 1187 |  |  |  |  |  |  | =item "IS" | 
| 1188 |  |  |  |  |  |  |  | 
| 1189 |  |  |  |  |  |  | =item "IS NOT" | 
| 1190 |  |  |  |  |  |  |  | 
| 1191 |  |  |  |  |  |  | This is useful for when you wish to match columns that contain NULL (or ones that don't). Use this operator and a value of "NULL". | 
| 1192 |  |  |  |  |  |  |  | 
| 1193 |  |  |  |  |  |  | =back | 
| 1194 |  |  |  |  |  |  |  | 
| 1195 |  |  |  |  |  |  | =item escape | 
| 1196 |  |  |  |  |  |  |  | 
| 1197 |  |  |  |  |  |  | If you need to escape wildcard characters (usually _ or %) in the value *explicitly* with | 
| 1198 |  |  |  |  |  |  | "ESCAPE", set the  escape character here. Note that backslashes may require special treatment | 
| 1199 |  |  |  |  |  |  | (e.g. Postgres dislikes \ or \\ in queries unless we use the E'' syntax). | 
| 1200 |  |  |  |  |  |  |  | 
| 1201 |  |  |  |  |  |  | =item entry_aggregator | 
| 1202 |  |  |  |  |  |  |  | 
| 1203 |  |  |  |  |  |  | Can be AND or OR (or anything else valid to aggregate two clauses in SQL) | 
| 1204 |  |  |  |  |  |  |  | 
| 1205 |  |  |  |  |  |  | =item case_sensitive | 
| 1206 |  |  |  |  |  |  |  | 
| 1207 |  |  |  |  |  |  | on some databases, such as postgres, setting case_sensitive to 1 will make | 
| 1208 |  |  |  |  |  |  | this search case sensitive.  Note that this flag is ignored if the column | 
| 1209 |  |  |  |  |  |  | is numeric. | 
| 1210 |  |  |  |  |  |  |  | 
| 1211 |  |  |  |  |  |  | =back | 
| 1212 |  |  |  |  |  |  |  | 
| 1213 |  |  |  |  |  |  | =cut | 
| 1214 |  |  |  |  |  |  |  | 
| 1215 |  |  |  |  |  |  | sub limit { | 
| 1216 | 279 |  |  | 279 | 1 | 28738 | my $self = shift; | 
| 1217 | 279 |  |  |  |  | 3691 | my %args = ( | 
| 1218 |  |  |  |  |  |  | table            => undef, | 
| 1219 |  |  |  |  |  |  | alias            => undef, | 
| 1220 |  |  |  |  |  |  | column           => undef, | 
| 1221 |  |  |  |  |  |  | value            => undef, | 
| 1222 |  |  |  |  |  |  | quote_value      => 1, | 
| 1223 |  |  |  |  |  |  | entry_aggregator => 'or', | 
| 1224 |  |  |  |  |  |  | case_sensitive   => undef, | 
| 1225 |  |  |  |  |  |  | operator         => '=', | 
| 1226 |  |  |  |  |  |  | escape           => undef, | 
| 1227 |  |  |  |  |  |  | subclause        => undef, | 
| 1228 |  |  |  |  |  |  | leftjoin         => undef, | 
| 1229 |  |  |  |  |  |  | @_    # get the real argumentlist | 
| 1230 |  |  |  |  |  |  | ); | 
| 1231 |  |  |  |  |  |  |  | 
| 1232 | 279 | 50 |  |  |  | 1960 | return if $self->derived; | 
| 1233 |  |  |  |  |  |  |  | 
| 1234 |  |  |  |  |  |  | #If we're performing a left join, we really want the alias to be the | 
| 1235 |  |  |  |  |  |  | #left join criterion. | 
| 1236 |  |  |  |  |  |  |  | 
| 1237 | 279 | 50 | 33 |  |  | 2491 | if (   ( defined $args{'leftjoin'} ) | 
| 1238 |  |  |  |  |  |  | && ( not defined $args{'alias'} ) ) | 
| 1239 |  |  |  |  |  |  | { | 
| 1240 | 0 |  |  |  |  | 0 | $args{'alias'} = $args{'leftjoin'}; | 
| 1241 |  |  |  |  |  |  | } | 
| 1242 |  |  |  |  |  |  |  | 
| 1243 |  |  |  |  |  |  | # {{{ if there's no alias set, we need to set it | 
| 1244 |  |  |  |  |  |  |  | 
| 1245 | 279 | 100 |  |  |  | 869 | unless ( defined $args{'alias'} ) { | 
| 1246 |  |  |  |  |  |  |  | 
| 1247 |  |  |  |  |  |  | #if the table we're looking at is the same as the main table | 
| 1248 | 271 | 50 | 33 |  |  | 955 | if ( !defined $args{'table'} || $args{'table'} eq $self->table ) { | 
| 1249 |  |  |  |  |  |  |  | 
| 1250 |  |  |  |  |  |  | # TODO this code assumes no self joins on that table. | 
| 1251 |  |  |  |  |  |  | # if someone can name a case where we'd want to do that, | 
| 1252 |  |  |  |  |  |  | # I'll change it. | 
| 1253 |  |  |  |  |  |  |  | 
| 1254 | 271 |  |  |  |  | 564 | $args{'alias'} = 'main'; | 
| 1255 |  |  |  |  |  |  | } | 
| 1256 |  |  |  |  |  |  |  | 
| 1257 |  |  |  |  |  |  | else { | 
| 1258 | 0 |  |  |  |  | 0 | $args{'alias'} = $self->new_alias( $args{'table'} ); | 
| 1259 |  |  |  |  |  |  | } | 
| 1260 |  |  |  |  |  |  | } | 
| 1261 |  |  |  |  |  |  |  | 
| 1262 |  |  |  |  |  |  | # }}} | 
| 1263 |  |  |  |  |  |  |  | 
| 1264 |  |  |  |  |  |  | # $column_obj is undefined when the table2 argument to the join is a table | 
| 1265 |  |  |  |  |  |  | # name and not a collection model class.  In that case, the class key | 
| 1266 |  |  |  |  |  |  | # doesn't exist for the join. | 
| 1267 | 279 | 50 | 66 |  |  | 1979 | my $class | 
| 1268 |  |  |  |  |  |  | = $self->{joins}{ $args{alias} } | 
| 1269 |  |  |  |  |  |  | && $self->{joins}{ $args{alias} }{class} | 
| 1270 |  |  |  |  |  |  | ? $self->{joins}{ $args{alias} }{class} | 
| 1271 |  |  |  |  |  |  | ->new( $self->_new_collection_args ) | 
| 1272 |  |  |  |  |  |  | : $self; | 
| 1273 | 279 |  |  |  |  | 767 | my $column_obj = $class->record_class->column( $args{column} ); | 
| 1274 |  |  |  |  |  |  |  | 
| 1275 | 279 | 100 | 100 |  |  | 1642 | $self->new_item->_apply_input_filters( | 
|  |  |  | 66 |  |  |  |  | 
| 1276 |  |  |  |  |  |  | column    => $column_obj, | 
| 1277 |  |  |  |  |  |  | value_ref => \$args{'value'}, | 
| 1278 |  |  |  |  |  |  | ) if $column_obj && $column_obj->encode_on_select && $args{operator} !~ /IS/; | 
| 1279 |  |  |  |  |  |  |  | 
| 1280 |  |  |  |  |  |  | # Ensure that the column has nothing fishy going on.  We can't | 
| 1281 |  |  |  |  |  |  | # simply check $column_obj's truth because joins mostly join by | 
| 1282 |  |  |  |  |  |  | # table name, not class, and we don't track table_name -> class. | 
| 1283 | 279 | 50 |  |  |  | 10883 | if ($args{column} =~ /\W/) { | 
| 1284 | 0 |  |  |  |  | 0 | warn "Possible SQL injection on column '$args{column}' in limit at @{[join(',',(caller)[1,2])]}\n"; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1285 | 0 |  |  |  |  | 0 | %args = ( | 
| 1286 |  |  |  |  |  |  | %args, | 
| 1287 |  |  |  |  |  |  | column   => 'id', | 
| 1288 |  |  |  |  |  |  | operator => '<', | 
| 1289 |  |  |  |  |  |  | value    => 0, | 
| 1290 |  |  |  |  |  |  | ); | 
| 1291 |  |  |  |  |  |  | } | 
| 1292 | 279 | 50 |  |  |  | 2162 | if ($args{operator} !~ /^(=|<|>|!=|<>|<=|>= | 
| 1293 |  |  |  |  |  |  | |(NOT\s*)?LIKE | 
| 1294 |  |  |  |  |  |  | |(NOT\s*)?(STARTS|ENDS)_?WITH | 
| 1295 |  |  |  |  |  |  | |(NOT\s*)?MATCHES | 
| 1296 |  |  |  |  |  |  | |IS(\s*NOT)? | 
| 1297 |  |  |  |  |  |  | |IN)$/ix) { | 
| 1298 | 0 |  |  |  |  | 0 | warn "Unknown operator '$args{operator}' in limit at  @{[join(',',(caller)[1,2])]}\n"; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1299 | 0 |  |  |  |  | 0 | %args = ( | 
| 1300 |  |  |  |  |  |  | %args, | 
| 1301 |  |  |  |  |  |  | column   => 'id', | 
| 1302 |  |  |  |  |  |  | operator => '<', | 
| 1303 |  |  |  |  |  |  | value    => 0, | 
| 1304 |  |  |  |  |  |  | ); | 
| 1305 |  |  |  |  |  |  | } | 
| 1306 |  |  |  |  |  |  |  | 
| 1307 |  |  |  |  |  |  |  | 
| 1308 |  |  |  |  |  |  | # Set this to the name of the column and the alias, unless we've been | 
| 1309 |  |  |  |  |  |  | # handed a subclause name | 
| 1310 | 279 | 50 |  |  |  | 1073 | my $qualified_column | 
| 1311 |  |  |  |  |  |  | = $args{'alias'} | 
| 1312 |  |  |  |  |  |  | ? $args{'alias'} . "." . $args{'column'} | 
| 1313 |  |  |  |  |  |  | : $args{'column'}; | 
| 1314 | 279 |  | 66 |  |  | 1112 | my $clause_id = $args{'subclause'} || $qualified_column; | 
| 1315 |  |  |  |  |  |  |  | 
| 1316 |  |  |  |  |  |  |  | 
| 1317 |  |  |  |  |  |  | # make passing in an object DTRT | 
| 1318 | 279 |  |  |  |  | 537 | my $value_ref = ref( $args{value} ); | 
| 1319 | 279 | 100 |  |  |  | 586 | if ($value_ref) { | 
| 1320 | 31 | 100 | 66 |  |  | 220 | if ( ( $value_ref ne 'ARRAY' ) | 
|  |  | 50 |  |  |  |  |  | 
| 1321 |  |  |  |  |  |  | && $args{value}->isa('Jifty::DBI::Record') ) | 
| 1322 |  |  |  |  |  |  | { | 
| 1323 | 2 | 50 | 33 |  |  | 15 | my $by = (defined $column_obj and defined $column_obj->by) | 
| 1324 |  |  |  |  |  |  | ? $column_obj->by | 
| 1325 |  |  |  |  |  |  | : 'id'; | 
| 1326 | 2 |  |  |  |  | 27 | $args{value} = $args{value}->$by; | 
| 1327 |  |  |  |  |  |  | } elsif ( $value_ref eq 'ARRAY' ) { | 
| 1328 |  |  |  |  |  |  |  | 
| 1329 |  |  |  |  |  |  | # Don't modify the original reference, it isn't polite | 
| 1330 | 29 |  |  |  |  | 42 | $args{value} = [ @{ $args{value} } ]; | 
|  | 29 |  |  |  |  | 121 |  | 
| 1331 | 62 | 100 | 66 |  |  | 295 | map { | 
| 1332 | 29 |  |  |  |  | 67 | my $by = (defined $column_obj and defined $column_obj->by) | 
| 1333 |  |  |  |  |  |  | ? $column_obj->by | 
| 1334 |  |  |  |  |  |  | : 'id'; | 
| 1335 | 62 | 100 | 66 |  |  | 607 | $_ = ( | 
| 1336 |  |  |  |  |  |  | ( ref $_ && $_->isa('Jifty::DBI::Record') ) | 
| 1337 |  |  |  |  |  |  | ? ( $_->$by ) | 
| 1338 |  |  |  |  |  |  | : $_ | 
| 1339 |  |  |  |  |  |  | ) | 
| 1340 | 29 |  |  |  |  | 53 | } @{ $args{value} }; | 
| 1341 |  |  |  |  |  |  | } | 
| 1342 |  |  |  |  |  |  | } | 
| 1343 |  |  |  |  |  |  |  | 
| 1344 |  |  |  |  |  |  | #since we're changing the search criteria, we need to redo the search | 
| 1345 | 279 |  |  |  |  | 854 | $self->redo_search(); | 
| 1346 |  |  |  |  |  |  |  | 
| 1347 |  |  |  |  |  |  | #If it's a like, we supply the %s around the search term | 
| 1348 | 279 | 100 |  |  |  | 5658 | if ( $args{'operator'} =~ /MATCHES/i ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1349 | 25 |  |  |  |  | 100 | $args{'value'} = "%" . $args{'value'} . "%"; | 
| 1350 |  |  |  |  |  |  | } elsif ( $args{'operator'} =~ /STARTS_?WITH/i ) { | 
| 1351 | 17 |  |  |  |  | 45 | $args{'value'} = $args{'value'} . "%"; | 
| 1352 |  |  |  |  |  |  | } elsif ( $args{'operator'} =~ /ENDS_?WITH/i ) { | 
| 1353 | 17 |  |  |  |  | 48 | $args{'value'} = "%" . $args{'value'}; | 
| 1354 |  |  |  |  |  |  | } | 
| 1355 | 279 |  |  |  |  | 6863 | $args{'operator'} =~ s/(?:MATCHES|ENDS_?WITH|STARTS_?WITH)/LIKE/i; | 
| 1356 |  |  |  |  |  |  |  | 
| 1357 |  |  |  |  |  |  | # Force the value to NULL (non-quoted) if the operator is IS. | 
| 1358 | 279 | 100 |  |  |  | 1106 | if ($args{'operator'} =~ /^IS(\s*NOT)?$/i) { | 
| 1359 | 54 |  |  |  |  | 108 | $args{'quote_value'} = 0; | 
| 1360 | 54 |  |  |  |  | 98 | $args{'value'} = 'NULL'; | 
| 1361 |  |  |  |  |  |  | } | 
| 1362 |  |  |  |  |  |  |  | 
| 1363 |  |  |  |  |  |  | # Quote the value | 
| 1364 | 279 | 100 |  |  |  | 757 | if ( $args{'quote_value'} ) { | 
| 1365 | 223 | 100 |  |  |  | 471 | if ( $value_ref eq 'ARRAY' ) { | 
| 1366 | 29 |  |  |  |  | 44 | map { $_ = $self->_handle->quote_value($_) } @{ $args{'value'} }; | 
|  | 62 |  |  |  |  | 189 |  | 
|  | 29 |  |  |  |  | 72 |  | 
| 1367 |  |  |  |  |  |  | } else { | 
| 1368 | 194 |  |  |  |  | 950 | $args{'value'} = $self->_handle->quote_value( $args{'value'} ); | 
| 1369 |  |  |  |  |  |  | } | 
| 1370 |  |  |  |  |  |  | } | 
| 1371 |  |  |  |  |  |  |  | 
| 1372 | 279 | 100 |  |  |  | 803 | if ( $args{'escape'} ) { | 
| 1373 | 4 |  |  |  |  | 19 | $args{'escape'} = 'ESCAPE ' . $self->_handle->quote_value( $args{escape} ); | 
| 1374 |  |  |  |  |  |  | } | 
| 1375 |  |  |  |  |  |  |  | 
| 1376 |  |  |  |  |  |  | # If we're trying to get a leftjoin restriction, lets set | 
| 1377 |  |  |  |  |  |  | # $restriction to point there. otherwise, lets construct normally | 
| 1378 |  |  |  |  |  |  |  | 
| 1379 | 279 |  |  |  |  | 400 | my $restriction; | 
| 1380 | 279 | 50 |  |  |  | 780 | if ( $args{'leftjoin'} ) { | 
| 1381 | 0 |  | 0 |  |  | 0 | $restriction | 
| 1382 |  |  |  |  |  |  | = $self->{'joins'}{ $args{'leftjoin'} }{'criteria'}{$clause_id} | 
| 1383 |  |  |  |  |  |  | ||= []; | 
| 1384 |  |  |  |  |  |  | } else { | 
| 1385 | 279 |  | 100 |  |  | 2534 | $restriction = $self->{'restrictions'}{$clause_id} ||= []; | 
| 1386 |  |  |  |  |  |  | } | 
| 1387 |  |  |  |  |  |  |  | 
| 1388 |  |  |  |  |  |  | # If it's a new value or we're overwriting this sort of restriction, | 
| 1389 |  |  |  |  |  |  |  | 
| 1390 | 279 | 100 | 66 |  |  | 1684 | if ( defined $args{'value'} && $args{'quote_value'} ) { | 
| 1391 | 223 |  |  |  |  | 321 | my $case_sensitive = 0; | 
| 1392 | 223 | 100 |  |  |  | 815 | if ( defined $args{'case_sensitive'} ) { | 
|  |  | 100 |  |  |  |  |  | 
| 1393 | 114 |  |  |  |  | 169 | $case_sensitive = $args{'case_sensitive'}; | 
| 1394 |  |  |  |  |  |  | } | 
| 1395 |  |  |  |  |  |  | elsif ( $column_obj ) { | 
| 1396 | 103 |  |  |  |  | 779 | $case_sensitive = $column_obj->case_sensitive; | 
| 1397 |  |  |  |  |  |  | } | 
| 1398 |  |  |  |  |  |  | # don't worry about case for numeric columns_in_db | 
| 1399 |  |  |  |  |  |  | # only be case insensitive when we KNOW it's a text | 
| 1400 | 223 | 100 | 100 |  |  | 2261 | if ( $column_obj && !$case_sensitive && !$column_obj->is_string ) { | 
|  |  |  | 100 |  |  |  |  | 
| 1401 | 68 |  |  |  |  | 98 | $case_sensitive = 1; | 
| 1402 |  |  |  |  |  |  | } | 
| 1403 |  |  |  |  |  |  |  | 
| 1404 | 223 | 100 | 66 |  |  | 1389 | if ( !$case_sensitive && $self->_handle->case_sensitive ) { | 
| 1405 | 67 |  |  |  |  | 240 | ( $qualified_column, $args{'operator'}, $args{'value'} ) | 
| 1406 |  |  |  |  |  |  | = $self->_handle->_make_clause_case_insensitive( | 
| 1407 |  |  |  |  |  |  | $qualified_column, $args{'operator'}, $args{'value'} ); | 
| 1408 |  |  |  |  |  |  | } | 
| 1409 |  |  |  |  |  |  | } | 
| 1410 |  |  |  |  |  |  |  | 
| 1411 | 279 | 100 |  |  |  | 734 | if ( $value_ref eq 'ARRAY' ) { | 
| 1412 | 29 | 50 |  |  |  | 149 | croak | 
| 1413 |  |  |  |  |  |  | 'Limits with an array ref are only allowed with operator \'IN\' or \'=\'' | 
| 1414 |  |  |  |  |  |  | unless $args{'operator'} =~ /^(IN|=)$/i; | 
| 1415 | 29 |  |  |  |  | 52 | $args{'value'} = '( ' . join( ',', @{ $args{'value'} } ) . ' )'; | 
|  | 29 |  |  |  |  | 121 |  | 
| 1416 | 29 |  |  |  |  | 73 | $args{'operator'} = 'IN'; | 
| 1417 |  |  |  |  |  |  | } | 
| 1418 |  |  |  |  |  |  |  | 
| 1419 | 279 |  |  |  |  | 1639 | my $clause = { | 
| 1420 |  |  |  |  |  |  | column   => $qualified_column, | 
| 1421 |  |  |  |  |  |  | operator => $args{'operator'}, | 
| 1422 |  |  |  |  |  |  | value    => $args{'value'}, | 
| 1423 |  |  |  |  |  |  | escape   => $args{'escape'}, | 
| 1424 |  |  |  |  |  |  | }; | 
| 1425 |  |  |  |  |  |  |  | 
| 1426 |  |  |  |  |  |  | # Juju because this should come _AFTER_ the EA | 
| 1427 | 279 |  |  |  |  | 400 | my @prefix; | 
| 1428 | 279 | 100 |  |  |  | 1031 | if ( $self->{'_open_parens'}{$clause_id} ) { | 
| 1429 | 1 |  |  |  |  | 4 | @prefix = ('(') x delete $self->{'_open_parens'}{$clause_id}; | 
| 1430 |  |  |  |  |  |  | } | 
| 1431 |  |  |  |  |  |  |  | 
| 1432 | 279 | 100 | 50 |  |  | 1780 | if ( lc( $args{'entry_aggregator'} || "" ) eq 'none' || !@$restriction ) { | 
|  |  |  | 66 |  |  |  |  | 
| 1433 | 274 |  |  |  |  | 1017 | @$restriction = ( @prefix, $clause ); | 
| 1434 |  |  |  |  |  |  | } else { | 
| 1435 | 5 |  |  |  |  | 18 | push @$restriction, $args{'entry_aggregator'}, @prefix, $clause; | 
| 1436 |  |  |  |  |  |  | } | 
| 1437 |  |  |  |  |  |  |  | 
| 1438 |  |  |  |  |  |  | # We're now limited. people can do searches. | 
| 1439 |  |  |  |  |  |  |  | 
| 1440 | 279 |  |  |  |  | 1021 | $self->_is_limited(1); | 
| 1441 |  |  |  |  |  |  |  | 
| 1442 | 279 | 50 |  |  |  | 1826 | if ( defined( $args{'alias'} ) ) { | 
| 1443 | 279 |  |  |  |  | 1368 | return ( $args{'alias'} ); | 
| 1444 |  |  |  |  |  |  | } else { | 
| 1445 | 0 |  |  |  |  | 0 | return (1); | 
| 1446 |  |  |  |  |  |  | } | 
| 1447 |  |  |  |  |  |  | } | 
| 1448 |  |  |  |  |  |  |  | 
| 1449 |  |  |  |  |  |  | =head2 open_paren CLAUSE | 
| 1450 |  |  |  |  |  |  |  | 
| 1451 |  |  |  |  |  |  | Places an open parenthesis at the current location in the given C. | 
| 1452 |  |  |  |  |  |  | Note that this can be used for Deep Magic, and has a high likelihood | 
| 1453 |  |  |  |  |  |  | of allowing you to construct malformed SQL queries.  Its interface | 
| 1454 |  |  |  |  |  |  | will probably change in the near future, but its presence allows for | 
| 1455 |  |  |  |  |  |  | arbitrarily complex queries. | 
| 1456 |  |  |  |  |  |  |  | 
| 1457 |  |  |  |  |  |  | Here's an example, to construct a SQL WHERE clause roughly equivalent to (depending on your SQL dialect): | 
| 1458 |  |  |  |  |  |  |  | 
| 1459 |  |  |  |  |  |  | parent = 12 AND task_type = 'action' | 
| 1460 |  |  |  |  |  |  | AND (status = 'open' | 
| 1461 |  |  |  |  |  |  | OR (status = 'done' | 
| 1462 |  |  |  |  |  |  | AND completed_on >= '2008-06-26 11:39:22')) | 
| 1463 |  |  |  |  |  |  |  | 
| 1464 |  |  |  |  |  |  | You can use sub-clauses and C and C as follows: | 
| 1465 |  |  |  |  |  |  |  | 
| 1466 |  |  |  |  |  |  | $col->limit( column => 'parent', value => 12 ); | 
| 1467 |  |  |  |  |  |  | $col->limit( column => 'task_type', value => 'action' ); | 
| 1468 |  |  |  |  |  |  |  | 
| 1469 |  |  |  |  |  |  | $col->open_paren("my_clause"); | 
| 1470 |  |  |  |  |  |  |  | 
| 1471 |  |  |  |  |  |  | $col->limit( subclause => "my_clause", column => 'status', value => 'open' ); | 
| 1472 |  |  |  |  |  |  |  | 
| 1473 |  |  |  |  |  |  | $col->open_paren("my_clause"); | 
| 1474 |  |  |  |  |  |  |  | 
| 1475 |  |  |  |  |  |  | $col->limit( subclause => "my_clause", column => 'status', | 
| 1476 |  |  |  |  |  |  | value => 'done', entry_aggregator => 'OR' ); | 
| 1477 |  |  |  |  |  |  | $col->limit( subclause => "my_clause", column => 'completed_on', | 
| 1478 |  |  |  |  |  |  | operator => '>=', value => '2008-06-26 11:39:22' ); | 
| 1479 |  |  |  |  |  |  |  | 
| 1480 |  |  |  |  |  |  | $col->close_paren("my_clause"); | 
| 1481 |  |  |  |  |  |  |  | 
| 1482 |  |  |  |  |  |  | $col->close_paren("my_clause"); | 
| 1483 |  |  |  |  |  |  |  | 
| 1484 |  |  |  |  |  |  | Where the C<"my_clause"> can be any name you choose. | 
| 1485 |  |  |  |  |  |  |  | 
| 1486 |  |  |  |  |  |  | =cut | 
| 1487 |  |  |  |  |  |  |  | 
| 1488 |  |  |  |  |  |  | sub open_paren { | 
| 1489 | 1 |  |  | 1 | 1 | 9 | my ( $self, $clause ) = @_; | 
| 1490 | 1 |  |  |  |  | 5 | $self->{_open_parens}{$clause}++; | 
| 1491 |  |  |  |  |  |  | } | 
| 1492 |  |  |  |  |  |  |  | 
| 1493 |  |  |  |  |  |  | =head2 close_paren CLAUSE | 
| 1494 |  |  |  |  |  |  |  | 
| 1495 |  |  |  |  |  |  | Places a close parenthesis at the current location in the given C. | 
| 1496 |  |  |  |  |  |  | Note that this can be used for Deep Magic, and has a high likelihood | 
| 1497 |  |  |  |  |  |  | of allowing you to construct malformed SQL queries.  Its interface | 
| 1498 |  |  |  |  |  |  | will probably change in the near future, but its presence allows for | 
| 1499 |  |  |  |  |  |  | arbitrarily complex queries. | 
| 1500 |  |  |  |  |  |  |  | 
| 1501 |  |  |  |  |  |  | =cut | 
| 1502 |  |  |  |  |  |  |  | 
| 1503 |  |  |  |  |  |  | # Immediate Action | 
| 1504 |  |  |  |  |  |  | sub close_paren { | 
| 1505 | 1 |  |  | 1 | 1 | 10 | my ( $self, $clause ) = @_; | 
| 1506 | 1 |  | 50 |  |  | 7 | my $restriction = $self->{'restrictions'}{$clause} ||= []; | 
| 1507 | 1 |  |  |  |  | 4 | push @$restriction, ')'; | 
| 1508 |  |  |  |  |  |  | } | 
| 1509 |  |  |  |  |  |  |  | 
| 1510 |  |  |  |  |  |  | sub _add_subclause { | 
| 1511 | 0 |  |  | 0 |  | 0 | my $self      = shift; | 
| 1512 | 0 |  |  |  |  | 0 | my $clauseid  = shift; | 
| 1513 | 0 |  |  |  |  | 0 | my $subclause = shift; | 
| 1514 |  |  |  |  |  |  |  | 
| 1515 | 0 |  |  |  |  | 0 | $self->{'subclauses'}{"$clauseid"} = $subclause; | 
| 1516 |  |  |  |  |  |  |  | 
| 1517 |  |  |  |  |  |  | } | 
| 1518 |  |  |  |  |  |  |  | 
| 1519 |  |  |  |  |  |  | sub _where_clause { | 
| 1520 | 324 |  |  | 324 |  | 457 | my $self         = shift; | 
| 1521 | 324 |  |  |  |  | 653 | my $where_clause = ''; | 
| 1522 |  |  |  |  |  |  |  | 
| 1523 |  |  |  |  |  |  | # Go through all the generic restrictions and build up the | 
| 1524 |  |  |  |  |  |  | # "generic_restrictions" subclause.  That's the only one that the | 
| 1525 |  |  |  |  |  |  | # collection builds itself.  Arguably, the abstraction should be | 
| 1526 |  |  |  |  |  |  | # better, but I don't really see where to put it. | 
| 1527 | 324 |  |  |  |  | 850 | $self->_compile_generic_restrictions(); | 
| 1528 |  |  |  |  |  |  |  | 
| 1529 |  |  |  |  |  |  | #Go through all restriction types. Build the where clause from the | 
| 1530 |  |  |  |  |  |  | #Various subclauses. | 
| 1531 |  |  |  |  |  |  |  | 
| 1532 | 324 |  |  |  |  | 2576 | my @subclauses = grep defined && length, | 
| 1533 | 324 |  | 66 |  |  | 590 | values %{ $self->{'subclauses'} }; | 
| 1534 |  |  |  |  |  |  |  | 
| 1535 | 324 | 100 |  |  |  | 1869 | $where_clause = " WHERE " . CORE::join( ' AND ', @subclauses ) | 
| 1536 |  |  |  |  |  |  | if (@subclauses); | 
| 1537 |  |  |  |  |  |  |  | 
| 1538 | 324 |  |  |  |  | 1130 | return ($where_clause); | 
| 1539 |  |  |  |  |  |  |  | 
| 1540 |  |  |  |  |  |  | } | 
| 1541 |  |  |  |  |  |  |  | 
| 1542 |  |  |  |  |  |  | #Compile the restrictions to a WHERE Clause | 
| 1543 |  |  |  |  |  |  |  | 
| 1544 |  |  |  |  |  |  | sub _compile_generic_restrictions { | 
| 1545 | 324 |  |  | 324 |  | 409 | my $self = shift; | 
| 1546 |  |  |  |  |  |  |  | 
| 1547 | 324 |  |  |  |  | 897 | delete $self->{'subclauses'}{'generic_restrictions'}; | 
| 1548 |  |  |  |  |  |  |  | 
| 1549 |  |  |  |  |  |  | # Go through all the restrictions of this type. Buld up the generic subclause | 
| 1550 | 324 |  |  |  |  | 458 | my $result = ''; | 
| 1551 | 324 |  | 33 |  |  | 687 | foreach my $restriction ( grep $_ && @$_, | 
|  | 324 |  |  |  |  | 2650 |  | 
| 1552 |  |  |  |  |  |  | values %{ $self->{'restrictions'} } ) | 
| 1553 |  |  |  |  |  |  | { | 
| 1554 | 312 | 100 |  |  |  | 751 | $result .= ' AND ' if $result; | 
| 1555 | 312 |  |  |  |  | 664 | $result .= '('; | 
| 1556 | 312 |  |  |  |  | 590 | foreach my $entry (@$restriction) { | 
| 1557 | 328 | 100 |  |  |  | 813 | unless ( ref $entry ) { | 
| 1558 | 10 |  |  |  |  | 24 | $result .= ' ' . $entry . ' '; | 
| 1559 |  |  |  |  |  |  | } else { | 
| 1560 | 1272 |  |  |  |  | 5726 | $result .= join ' ', | 
| 1561 | 318 |  |  |  |  | 834 | grep {defined} | 
| 1562 | 318 |  |  |  |  | 684 | @{$entry}{qw(column operator value escape)}; | 
| 1563 |  |  |  |  |  |  | } | 
| 1564 |  |  |  |  |  |  | } | 
| 1565 | 312 |  |  |  |  | 742 | $result .= ')'; | 
| 1566 |  |  |  |  |  |  | } | 
| 1567 | 324 |  |  |  |  | 1287 | return ( $self->{'subclauses'}{'generic_restrictions'} = $result ); | 
| 1568 |  |  |  |  |  |  | } | 
| 1569 |  |  |  |  |  |  |  | 
| 1570 |  |  |  |  |  |  | # set $self->{$type .'_clause'} to new value | 
| 1571 |  |  |  |  |  |  | # redo_search only if new value is really new | 
| 1572 |  |  |  |  |  |  | sub _set_clause { | 
| 1573 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 1574 | 0 |  |  |  |  | 0 | my ( $type, $value ) = @_; | 
| 1575 | 0 |  |  |  |  | 0 | $type .= '_clause'; | 
| 1576 | 0 | 0 | 0 |  |  | 0 | if ( ( $self->{$type} || '' ) ne ( $value || '' ) ) { | 
|  |  |  | 0 |  |  |  |  | 
| 1577 | 0 |  |  |  |  | 0 | $self->redo_search; | 
| 1578 |  |  |  |  |  |  | } | 
| 1579 | 0 |  |  |  |  | 0 | $self->{$type} = $value; | 
| 1580 |  |  |  |  |  |  | } | 
| 1581 |  |  |  |  |  |  |  | 
| 1582 |  |  |  |  |  |  | # stub for back-compat | 
| 1583 |  |  |  |  |  |  | sub _quote_value { | 
| 1584 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 1585 | 0 |  |  |  |  | 0 | return $self->_handle->quote_value(@_); | 
| 1586 |  |  |  |  |  |  | } | 
| 1587 |  |  |  |  |  |  |  | 
| 1588 |  |  |  |  |  |  | =head2 order_by_cols DEPRECATED | 
| 1589 |  |  |  |  |  |  |  | 
| 1590 |  |  |  |  |  |  | *DEPRECATED*. Use C method. | 
| 1591 |  |  |  |  |  |  |  | 
| 1592 |  |  |  |  |  |  | =cut | 
| 1593 |  |  |  |  |  |  |  | 
| 1594 |  |  |  |  |  |  | sub order_by_cols { | 
| 1595 | 0 |  |  | 0 | 1 | 0 | require Carp; | 
| 1596 | 0 |  |  |  |  | 0 | Carp::cluck("order_by_cols is deprecated, use order_by method"); | 
| 1597 | 0 |  |  |  |  | 0 | goto &order_by; | 
| 1598 |  |  |  |  |  |  | } | 
| 1599 |  |  |  |  |  |  |  | 
| 1600 |  |  |  |  |  |  | =head2 order_by EMPTY|HASH|ARRAY_OF_HASHES | 
| 1601 |  |  |  |  |  |  |  | 
| 1602 |  |  |  |  |  |  | Orders the returned results by column(s) and/or function(s) on column(s). | 
| 1603 |  |  |  |  |  |  |  | 
| 1604 |  |  |  |  |  |  | Takes a paramhash of C, C and C | 
| 1605 |  |  |  |  |  |  | or C and C. | 
| 1606 |  |  |  |  |  |  | C defaults to main. | 
| 1607 |  |  |  |  |  |  | C defaults to ASC(ending), DES(cending) is also a valid value. | 
| 1608 |  |  |  |  |  |  | C and C have no default values. | 
| 1609 |  |  |  |  |  |  |  | 
| 1610 |  |  |  |  |  |  | Use C instead of C and C to order by | 
| 1611 |  |  |  |  |  |  | the function value. Note that if you want use a column as argument of | 
| 1612 |  |  |  |  |  |  | the function then you have to build correct reference with alias | 
| 1613 |  |  |  |  |  |  | in the C format. | 
| 1614 |  |  |  |  |  |  |  | 
| 1615 |  |  |  |  |  |  | If you specify C and C, the column (and C) will be | 
| 1616 |  |  |  |  |  |  | wrapped in the function.  This is useful for simple functions like C or | 
| 1617 |  |  |  |  |  |  | C. | 
| 1618 |  |  |  |  |  |  |  | 
| 1619 |  |  |  |  |  |  | Use array of hashes to order by many columns/functions. | 
| 1620 |  |  |  |  |  |  |  | 
| 1621 |  |  |  |  |  |  | Calling this I the ordering, it doesn't refine it. If you want to keep | 
| 1622 |  |  |  |  |  |  | previous ordering, use C. | 
| 1623 |  |  |  |  |  |  |  | 
| 1624 |  |  |  |  |  |  | The results would be unordered if method called without arguments. | 
| 1625 |  |  |  |  |  |  |  | 
| 1626 |  |  |  |  |  |  | Returns the current list of columns. | 
| 1627 |  |  |  |  |  |  |  | 
| 1628 |  |  |  |  |  |  | =cut | 
| 1629 |  |  |  |  |  |  |  | 
| 1630 |  |  |  |  |  |  | sub order_by { | 
| 1631 | 7 |  |  | 7 | 1 | 39 | my $self = shift; | 
| 1632 | 7 | 50 |  |  |  | 34 | return if $self->derived; | 
| 1633 | 7 | 100 |  |  |  | 198 | if (@_) { | 
| 1634 | 6 |  |  |  |  | 17 | $self->{'order_by'} = []; | 
| 1635 | 6 |  |  |  |  | 43 | $self->add_order_by(@_); | 
| 1636 |  |  |  |  |  |  | } | 
| 1637 | 7 |  | 50 |  |  | 41 | return ( $self->{'order_by'} || [] ); | 
| 1638 |  |  |  |  |  |  | } | 
| 1639 |  |  |  |  |  |  |  | 
| 1640 |  |  |  |  |  |  | =head2 add_order_by EMPTY|HASH|ARRAY_OF_HASHES | 
| 1641 |  |  |  |  |  |  |  | 
| 1642 |  |  |  |  |  |  | Same as order_by, except it will not reset the ordering you have already set. | 
| 1643 |  |  |  |  |  |  |  | 
| 1644 |  |  |  |  |  |  | =cut | 
| 1645 |  |  |  |  |  |  |  | 
| 1646 |  |  |  |  |  |  | sub add_order_by { | 
| 1647 | 8 |  |  | 8 | 1 | 35 | my $self = shift; | 
| 1648 | 8 | 50 |  |  |  | 31 | return if $self->derived; | 
| 1649 | 8 | 50 |  |  |  | 69 | if (@_) { | 
| 1650 | 8 |  |  |  |  | 24 | my @args = @_; | 
| 1651 |  |  |  |  |  |  |  | 
| 1652 | 8 | 100 |  |  |  | 65 | unless ( UNIVERSAL::isa( $args[0], 'HASH' ) ) { | 
| 1653 | 7 |  |  |  |  | 41 | @args = {@args}; | 
| 1654 |  |  |  |  |  |  | } | 
| 1655 | 8 |  | 50 |  |  | 15 | push @{ $self->{'order_by'} ||= [] }, @args; | 
|  | 8 |  |  |  |  | 37 |  | 
| 1656 | 8 |  |  |  |  | 25 | $self->redo_search(); | 
| 1657 |  |  |  |  |  |  | } | 
| 1658 | 8 |  | 50 |  |  | 12310 | return ( $self->{'order_by'} || [] ); | 
| 1659 |  |  |  |  |  |  | } | 
| 1660 |  |  |  |  |  |  |  | 
| 1661 |  |  |  |  |  |  | =head2 clear_order_by | 
| 1662 |  |  |  |  |  |  |  | 
| 1663 |  |  |  |  |  |  | Clears whatever would normally get set in the ORDER BY clause. | 
| 1664 |  |  |  |  |  |  |  | 
| 1665 |  |  |  |  |  |  | =cut | 
| 1666 |  |  |  |  |  |  |  | 
| 1667 |  |  |  |  |  |  | sub clear_order_by { | 
| 1668 | 1 |  |  | 1 | 1 | 2 | my $self = shift; | 
| 1669 |  |  |  |  |  |  |  | 
| 1670 | 1 |  |  |  |  | 4 | $self->{'order_by'} = []; | 
| 1671 |  |  |  |  |  |  | } | 
| 1672 |  |  |  |  |  |  |  | 
| 1673 |  |  |  |  |  |  | =head2 _order_clause | 
| 1674 |  |  |  |  |  |  |  | 
| 1675 |  |  |  |  |  |  | returns the ORDER BY clause for the search. | 
| 1676 |  |  |  |  |  |  |  | 
| 1677 |  |  |  |  |  |  | =cut | 
| 1678 |  |  |  |  |  |  |  | 
| 1679 |  |  |  |  |  |  | sub _order_clause { | 
| 1680 | 256 |  |  | 256 |  | 376 | my $self = shift; | 
| 1681 |  |  |  |  |  |  |  | 
| 1682 | 256 | 100 |  |  |  | 1055 | return '' unless $self->{'order_by'}; | 
| 1683 |  |  |  |  |  |  |  | 
| 1684 | 13 |  |  |  |  | 18 | my $clause = ''; | 
| 1685 | 13 |  |  |  |  | 19 | foreach my $row ( @{ $self->{'order_by'} } ) { | 
|  | 13 |  |  |  |  | 34 |  | 
| 1686 |  |  |  |  |  |  |  | 
| 1687 | 29 |  |  |  |  | 139 | my %rowhash = ( | 
| 1688 |  |  |  |  |  |  | alias  => 'main', | 
| 1689 |  |  |  |  |  |  | column => undef, | 
| 1690 |  |  |  |  |  |  | order  => 'ASC', | 
| 1691 |  |  |  |  |  |  | %$row | 
| 1692 |  |  |  |  |  |  | ); | 
| 1693 | 29 | 100 |  |  |  | 92 | if ( $rowhash{'order'} =~ /^des/i ) { | 
| 1694 | 12 |  |  |  |  | 23 | $rowhash{'order'} = "DESC"; | 
| 1695 |  |  |  |  |  |  | } else { | 
| 1696 | 17 |  |  |  |  | 25 | $rowhash{'order'} = "ASC"; | 
| 1697 |  |  |  |  |  |  | } | 
| 1698 |  |  |  |  |  |  |  | 
| 1699 | 29 | 100 | 100 |  |  | 187 | if ( $rowhash{'function'} and not defined $rowhash{'column'} ) { | 
|  |  | 50 | 33 |  |  |  |  | 
| 1700 | 6 | 100 |  |  |  | 14 | $clause .= ( $clause ? ", " : " " ); | 
| 1701 | 6 |  |  |  |  | 12 | $clause .= $rowhash{'function'} . ' '; | 
| 1702 | 6 |  |  |  |  | 16 | $clause .= $rowhash{'order'}; | 
| 1703 |  |  |  |  |  |  |  | 
| 1704 |  |  |  |  |  |  | } elsif ( ( defined $rowhash{'alias'} ) | 
| 1705 |  |  |  |  |  |  | and ( $rowhash{'column'} ) ) | 
| 1706 |  |  |  |  |  |  | { | 
| 1707 | 23 | 50 |  |  |  | 117 | if ($rowhash{'column'} =~ /\W/) { | 
| 1708 | 0 |  |  |  |  | 0 | warn "Possible SQL injection in column '$rowhash{column}' in order_by\n"; | 
| 1709 | 0 |  |  |  |  | 0 | next; | 
| 1710 |  |  |  |  |  |  | } | 
| 1711 |  |  |  |  |  |  |  | 
| 1712 | 23 | 100 |  |  |  | 53 | $clause .= ( $clause ? ", " : " " ); | 
| 1713 | 23 | 100 |  |  |  | 51 | $clause .= $rowhash{'function'} . "(" if $rowhash{'function'}; | 
| 1714 | 23 | 100 |  |  |  | 71 | $clause .= $rowhash{'alias'} . "." if $rowhash{'alias'}; | 
| 1715 | 23 |  |  |  |  | 28 | $clause .= $rowhash{'column'}; | 
| 1716 | 23 | 100 |  |  |  | 52 | $clause .= ")" if $rowhash{'function'}; | 
| 1717 | 23 |  |  |  |  | 63 | $clause .= " " . $rowhash{'order'}; | 
| 1718 |  |  |  |  |  |  | } | 
| 1719 |  |  |  |  |  |  | } | 
| 1720 | 13 | 100 |  |  |  | 46 | $clause = " ORDER BY$clause " if $clause; | 
| 1721 | 13 |  |  |  |  | 53 | return $clause; | 
| 1722 |  |  |  |  |  |  | } | 
| 1723 |  |  |  |  |  |  |  | 
| 1724 |  |  |  |  |  |  | =head2 group_by_cols DEPRECATED | 
| 1725 |  |  |  |  |  |  |  | 
| 1726 |  |  |  |  |  |  | *DEPRECATED*. Use group_by method. | 
| 1727 |  |  |  |  |  |  |  | 
| 1728 |  |  |  |  |  |  | =cut | 
| 1729 |  |  |  |  |  |  |  | 
| 1730 |  |  |  |  |  |  | sub group_by_cols { | 
| 1731 | 0 |  |  | 0 | 1 | 0 | require Carp; | 
| 1732 | 0 |  |  |  |  | 0 | Carp::cluck("group_by_cols is deprecated, use group_by method"); | 
| 1733 | 0 |  |  |  |  | 0 | goto &group_by; | 
| 1734 |  |  |  |  |  |  | } | 
| 1735 |  |  |  |  |  |  |  | 
| 1736 |  |  |  |  |  |  | =head2 group_by EMPTY|HASH|ARRAY_OF_HASHES | 
| 1737 |  |  |  |  |  |  |  | 
| 1738 |  |  |  |  |  |  | Groups the search results by column(s) and/or function(s) on column(s). | 
| 1739 |  |  |  |  |  |  |  | 
| 1740 |  |  |  |  |  |  | Takes a paramhash of C and C or C. | 
| 1741 |  |  |  |  |  |  | C defaults to main. | 
| 1742 |  |  |  |  |  |  | C and C have no default values. | 
| 1743 |  |  |  |  |  |  |  | 
| 1744 |  |  |  |  |  |  | Use C instead of C and C to group by | 
| 1745 |  |  |  |  |  |  | the function value. Note that if you want use a column as argument | 
| 1746 |  |  |  |  |  |  | of the function then you have to build correct reference with alias | 
| 1747 |  |  |  |  |  |  | in the C format. | 
| 1748 |  |  |  |  |  |  |  | 
| 1749 |  |  |  |  |  |  | Use array of hashes to group by many columns/functions. | 
| 1750 |  |  |  |  |  |  |  | 
| 1751 |  |  |  |  |  |  | The method is EXPERIMENTAL and subject to change. | 
| 1752 |  |  |  |  |  |  |  | 
| 1753 |  |  |  |  |  |  | =cut | 
| 1754 |  |  |  |  |  |  |  | 
| 1755 |  |  |  |  |  |  | sub group_by { | 
| 1756 | 2 |  |  | 2 | 1 | 34 | my $self = shift; | 
| 1757 |  |  |  |  |  |  |  | 
| 1758 | 2 | 50 |  |  |  | 11 | return if $self->derived; | 
| 1759 | 2 |  |  |  |  | 20 | my @args = @_; | 
| 1760 |  |  |  |  |  |  |  | 
| 1761 | 2 | 50 |  |  |  | 17 | unless ( UNIVERSAL::isa( $args[0], 'HASH' ) ) { | 
| 1762 | 2 |  |  |  |  | 9 | @args = {@args}; | 
| 1763 |  |  |  |  |  |  | } | 
| 1764 | 2 |  |  |  |  | 7 | $self->{'group_by'} = \@args; | 
| 1765 | 2 |  |  |  |  | 93 | $self->redo_search(); | 
| 1766 |  |  |  |  |  |  | } | 
| 1767 |  |  |  |  |  |  |  | 
| 1768 |  |  |  |  |  |  | =head2 _group_clause | 
| 1769 |  |  |  |  |  |  |  | 
| 1770 |  |  |  |  |  |  | Private function to return the "GROUP BY" clause for this query. | 
| 1771 |  |  |  |  |  |  |  | 
| 1772 |  |  |  |  |  |  | =cut | 
| 1773 |  |  |  |  |  |  |  | 
| 1774 |  |  |  |  |  |  | sub _group_clause { | 
| 1775 | 247 |  |  | 247 |  | 427 | my $self = shift; | 
| 1776 | 247 | 100 |  |  |  | 956 | return '' unless $self->{'group_by'}; | 
| 1777 |  |  |  |  |  |  |  | 
| 1778 | 3 |  |  |  |  | 12 | my $row; | 
| 1779 |  |  |  |  |  |  | my $clause; | 
| 1780 |  |  |  |  |  |  |  | 
| 1781 | 3 |  |  |  |  | 6 | foreach $row ( @{ $self->{'group_by'} } ) { | 
|  | 3 |  |  |  |  | 11 |  | 
| 1782 | 3 |  |  |  |  | 18 | my %rowhash = ( | 
| 1783 |  |  |  |  |  |  | alias => 'main', | 
| 1784 |  |  |  |  |  |  |  | 
| 1785 |  |  |  |  |  |  | column => undef, | 
| 1786 |  |  |  |  |  |  | %$row | 
| 1787 |  |  |  |  |  |  | ); | 
| 1788 | 3 | 50 | 33 |  |  | 40 | if ( $rowhash{'function'} ) { | 
|  |  | 50 |  |  |  |  |  | 
| 1789 | 0 | 0 |  |  |  | 0 | $clause .= ( $clause ? ", " : " " ); | 
| 1790 | 0 |  |  |  |  | 0 | $clause .= $rowhash{'function'}; | 
| 1791 |  |  |  |  |  |  |  | 
| 1792 |  |  |  |  |  |  | } elsif ( ( $rowhash{'alias'} ) | 
| 1793 |  |  |  |  |  |  | and ( $rowhash{'column'} ) ) | 
| 1794 |  |  |  |  |  |  | { | 
| 1795 | 3 | 50 |  |  |  | 22 | if ($rowhash{'column'} =~ /\W/) { | 
| 1796 | 0 |  |  |  |  | 0 | warn "Possible SQL injection in column '$rowhash{column}' in group_by\n"; | 
| 1797 | 0 |  |  |  |  | 0 | next; | 
| 1798 |  |  |  |  |  |  | } | 
| 1799 |  |  |  |  |  |  |  | 
| 1800 | 3 | 50 |  |  |  | 13 | $clause .= ( $clause ? ", " : " " ); | 
| 1801 | 3 |  |  |  |  | 7 | $clause .= $rowhash{'alias'} . "."; | 
| 1802 | 3 |  |  |  |  | 10 | $clause .= $rowhash{'column'}; | 
| 1803 |  |  |  |  |  |  | } | 
| 1804 |  |  |  |  |  |  | } | 
| 1805 | 3 | 50 |  |  |  | 13 | if ($clause) { | 
| 1806 | 3 |  |  |  |  | 11 | return " GROUP BY" . $clause . " "; | 
| 1807 |  |  |  |  |  |  | } else { | 
| 1808 | 0 |  |  |  |  | 0 | return ''; | 
| 1809 |  |  |  |  |  |  | } | 
| 1810 |  |  |  |  |  |  | } | 
| 1811 |  |  |  |  |  |  |  | 
| 1812 |  |  |  |  |  |  | =head2 new_alias table_OR_CLASS | 
| 1813 |  |  |  |  |  |  |  | 
| 1814 |  |  |  |  |  |  | Takes the name of a table or a Jifty::DBI::Record subclass. | 
| 1815 |  |  |  |  |  |  | Returns the string of a new Alias for that table, which can be used | 
| 1816 |  |  |  |  |  |  | to Join tables or to limit what gets found by | 
| 1817 |  |  |  |  |  |  | a search. | 
| 1818 |  |  |  |  |  |  |  | 
| 1819 |  |  |  |  |  |  | =cut | 
| 1820 |  |  |  |  |  |  |  | 
| 1821 |  |  |  |  |  |  | sub new_alias { | 
| 1822 | 5 |  |  | 5 | 1 | 13 | my $self = shift; | 
| 1823 | 5 |  | 50 |  |  | 20 | my $refers_to = shift || die "Missing parameter"; | 
| 1824 | 5 |  |  |  |  | 9 | my $table; | 
| 1825 | 5 |  |  |  |  | 9 | my $class = undef; | 
| 1826 | 5 | 100 |  |  |  | 49 | if ( $refers_to->can('table') ) { | 
| 1827 | 2 |  |  |  |  | 11 | $table = $refers_to->table; | 
| 1828 | 2 |  |  |  |  | 14 | $class = $refers_to; | 
| 1829 |  |  |  |  |  |  | } else { | 
| 1830 | 3 |  |  |  |  | 8 | $table = $refers_to; | 
| 1831 |  |  |  |  |  |  | } | 
| 1832 |  |  |  |  |  |  |  | 
| 1833 | 5 |  |  |  |  | 29 | my $alias = $self->_get_alias($table); | 
| 1834 |  |  |  |  |  |  |  | 
| 1835 | 5 | 100 |  |  |  | 60 | $self->{'joins'}{$alias} = { | 
| 1836 |  |  |  |  |  |  | alias => $alias, | 
| 1837 |  |  |  |  |  |  | table => $table, | 
| 1838 |  |  |  |  |  |  | type  => 'CROSS', | 
| 1839 |  |  |  |  |  |  | ( $class ? ( class => $class ) : () ), | 
| 1840 |  |  |  |  |  |  | alias_string => " CROSS JOIN $table $alias ", | 
| 1841 |  |  |  |  |  |  | }; | 
| 1842 |  |  |  |  |  |  |  | 
| 1843 | 5 |  |  |  |  | 17 | return $alias; | 
| 1844 |  |  |  |  |  |  | } | 
| 1845 |  |  |  |  |  |  |  | 
| 1846 |  |  |  |  |  |  | # _get_alias is a private function which takes an tablename and | 
| 1847 |  |  |  |  |  |  | # returns a new alias for that table without adding something to | 
| 1848 |  |  |  |  |  |  | # self->{'joins'}.  This function is used by new_alias and the | 
| 1849 |  |  |  |  |  |  | # as-yet-unnamed left join code | 
| 1850 |  |  |  |  |  |  |  | 
| 1851 |  |  |  |  |  |  | sub _get_alias { | 
| 1852 | 14 |  |  | 14 |  | 27 | my $self  = shift; | 
| 1853 | 14 |  |  |  |  | 22 | my $table = shift; | 
| 1854 |  |  |  |  |  |  |  | 
| 1855 | 14 |  |  |  |  | 70 | return $table . "_" . ++$self->{'alias_count'}; | 
| 1856 |  |  |  |  |  |  | } | 
| 1857 |  |  |  |  |  |  |  | 
| 1858 |  |  |  |  |  |  | =head2 join | 
| 1859 |  |  |  |  |  |  |  | 
| 1860 |  |  |  |  |  |  | Join instructs Jifty::DBI::Collection to join two tables. | 
| 1861 |  |  |  |  |  |  |  | 
| 1862 |  |  |  |  |  |  | The standard form takes a paramhash with keys C, C, C | 
| 1863 |  |  |  |  |  |  | and C. C and C are column aliases obtained from | 
| 1864 |  |  |  |  |  |  | $self->new_alias or a $self->limit. C and C are the columns | 
| 1865 |  |  |  |  |  |  | in C and C that should be linked, respectively.  For this | 
| 1866 |  |  |  |  |  |  | type of join, this method has no return value. | 
| 1867 |  |  |  |  |  |  |  | 
| 1868 |  |  |  |  |  |  | Supplying the parameter C => 'left' causes Join to perform a left | 
| 1869 |  |  |  |  |  |  | join.  in this case, it takes C, C, C and | 
| 1870 |  |  |  |  |  |  | C. Because of the way that left joins work, this method needs a | 
| 1871 |  |  |  |  |  |  | table for the second column rather than merely an alias.  For this type | 
| 1872 |  |  |  |  |  |  | of join, it will return the alias generated by the join. | 
| 1873 |  |  |  |  |  |  |  | 
| 1874 |  |  |  |  |  |  | The parameter C defaults C<=>, but you can specify other | 
| 1875 |  |  |  |  |  |  | operators to join with. | 
| 1876 |  |  |  |  |  |  |  | 
| 1877 |  |  |  |  |  |  | Passing a true value for the C parameter allows one to | 
| 1878 |  |  |  |  |  |  | specify that, despite the join, the original table's rows are will all | 
| 1879 |  |  |  |  |  |  | still be distinct. | 
| 1880 |  |  |  |  |  |  |  | 
| 1881 |  |  |  |  |  |  | Instead of C/C, it's possible to specify expression, to join | 
| 1882 |  |  |  |  |  |  | C/C on an arbitrary expression. | 
| 1883 |  |  |  |  |  |  |  | 
| 1884 |  |  |  |  |  |  | =cut | 
| 1885 |  |  |  |  |  |  |  | 
| 1886 |  |  |  |  |  |  | sub join { | 
| 1887 | 12 |  |  | 12 | 1 | 894 | my $self = shift; | 
| 1888 | 12 |  |  |  |  | 518 | my %args = ( | 
| 1889 |  |  |  |  |  |  | type    => 'normal', | 
| 1890 |  |  |  |  |  |  | column1 => undef, | 
| 1891 |  |  |  |  |  |  | alias1  => 'main', | 
| 1892 |  |  |  |  |  |  | table2  => undef, | 
| 1893 |  |  |  |  |  |  | column2 => undef, | 
| 1894 |  |  |  |  |  |  | alias2  => undef, | 
| 1895 |  |  |  |  |  |  | @_ | 
| 1896 |  |  |  |  |  |  | ); | 
| 1897 |  |  |  |  |  |  |  | 
| 1898 | 12 | 50 |  |  |  | 74 | return if $self->derived; | 
| 1899 | 12 |  |  |  |  | 113 | $self->_handle->join( collection => $self, %args ); | 
| 1900 |  |  |  |  |  |  |  | 
| 1901 |  |  |  |  |  |  | } | 
| 1902 |  |  |  |  |  |  |  | 
| 1903 |  |  |  |  |  |  | =head2 set_page_info [per_page => NUMBER,] [current_page => NUMBER] | 
| 1904 |  |  |  |  |  |  |  | 
| 1905 |  |  |  |  |  |  | Sets the current page (one-based) and number of items per page on the | 
| 1906 |  |  |  |  |  |  | pager object, and pulls the number of elements from the collection. | 
| 1907 |  |  |  |  |  |  | This both sets up the collection's L object so that you | 
| 1908 |  |  |  |  |  |  | can use its calculations, and sets the L | 
| 1909 |  |  |  |  |  |  | C and C so that queries return values from | 
| 1910 |  |  |  |  |  |  | the selected page. | 
| 1911 |  |  |  |  |  |  |  | 
| 1912 |  |  |  |  |  |  | If a C of C is passed, then paging is basically disabled | 
| 1913 |  |  |  |  |  |  | (by setting C to the number of entries, and C to 1) | 
| 1914 |  |  |  |  |  |  |  | 
| 1915 |  |  |  |  |  |  | =cut | 
| 1916 |  |  |  |  |  |  |  | 
| 1917 |  |  |  |  |  |  | sub set_page_info { | 
| 1918 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 1919 | 0 |  |  |  |  | 0 | my %args = ( | 
| 1920 |  |  |  |  |  |  | per_page     => 0, | 
| 1921 |  |  |  |  |  |  | current_page => 1,    # 1-based | 
| 1922 |  |  |  |  |  |  | @_ | 
| 1923 |  |  |  |  |  |  | ); | 
| 1924 | 0 | 0 |  |  |  | 0 | return if $self->derived; | 
| 1925 |  |  |  |  |  |  |  | 
| 1926 | 0 |  |  |  |  | 0 | my $weakself = $self; | 
| 1927 | 0 |  |  |  |  | 0 | weaken($weakself); | 
| 1928 |  |  |  |  |  |  |  | 
| 1929 | 0 |  |  | 0 |  | 0 | my $total_entries = lazy { $weakself->count_all }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1930 |  |  |  |  |  |  |  | 
| 1931 | 0 | 0 |  |  |  | 0 | if ($args{'current_page'} eq 'all') { | 
| 1932 | 0 |  |  |  |  | 0 | $args{'current_page'} = 1; | 
| 1933 | 0 |  |  |  |  | 0 | $args{'per_page'}     = $total_entries; | 
| 1934 |  |  |  |  |  |  | } | 
| 1935 |  |  |  |  |  |  |  | 
| 1936 | 0 |  |  |  |  | 0 | $self->pager->total_entries($total_entries) | 
| 1937 |  |  |  |  |  |  | ->entries_per_page( $args{'per_page'} ) | 
| 1938 |  |  |  |  |  |  | ->current_page( $args{'current_page'} ); | 
| 1939 |  |  |  |  |  |  |  | 
| 1940 | 0 |  |  |  |  | 0 | $self->rows_per_page( $args{'per_page'} ); | 
| 1941 |  |  |  |  |  |  |  | 
| 1942 |  |  |  |  |  |  | # We're not using $pager->first because it automatically does a count_all | 
| 1943 |  |  |  |  |  |  | # to correctly return '0' for empty collections | 
| 1944 | 0 |  |  |  |  | 0 | $self->first_row( ( $args{'current_page'} - 1 ) * $args{'per_page'} + 1 ); | 
| 1945 |  |  |  |  |  |  |  | 
| 1946 |  |  |  |  |  |  | } | 
| 1947 |  |  |  |  |  |  |  | 
| 1948 |  |  |  |  |  |  | =head2 rows_per_page | 
| 1949 |  |  |  |  |  |  |  | 
| 1950 |  |  |  |  |  |  | limits the number of rows returned by the database.  Optionally, takes | 
| 1951 |  |  |  |  |  |  | an integer which restricts the # of rows returned in a result Returns | 
| 1952 |  |  |  |  |  |  | the number of rows the database should display. | 
| 1953 |  |  |  |  |  |  |  | 
| 1954 |  |  |  |  |  |  | =cut | 
| 1955 |  |  |  |  |  |  |  | 
| 1956 |  |  |  |  |  |  | =head2 first_row | 
| 1957 |  |  |  |  |  |  |  | 
| 1958 |  |  |  |  |  |  | Get or set the first row of the result set the database should return. | 
| 1959 |  |  |  |  |  |  | Takes an optional single integer argument. Returns the currently set | 
| 1960 |  |  |  |  |  |  | integer first row that the database should return. | 
| 1961 |  |  |  |  |  |  |  | 
| 1962 |  |  |  |  |  |  |  | 
| 1963 |  |  |  |  |  |  | =cut | 
| 1964 |  |  |  |  |  |  |  | 
| 1965 |  |  |  |  |  |  | # returns the first row | 
| 1966 |  |  |  |  |  |  | sub first_row { | 
| 1967 | 246 |  |  | 246 | 1 | 2621 | my $self = shift; | 
| 1968 | 246 | 50 |  |  |  | 653 | if (@_) { | 
| 1969 | 0 |  |  |  |  | 0 | $self->{'first_row'} = shift; | 
| 1970 |  |  |  |  |  |  |  | 
| 1971 |  |  |  |  |  |  | #SQL starts counting at 0 | 
| 1972 | 0 |  |  |  |  | 0 | $self->{'first_row'}--; | 
| 1973 |  |  |  |  |  |  |  | 
| 1974 |  |  |  |  |  |  | #gotta redo the search if changing pages | 
| 1975 | 0 |  |  |  |  | 0 | $self->redo_search(); | 
| 1976 |  |  |  |  |  |  | } | 
| 1977 | 246 |  |  |  |  | 1224 | return ( $self->{'first_row'} ); | 
| 1978 |  |  |  |  |  |  | } | 
| 1979 |  |  |  |  |  |  |  | 
| 1980 |  |  |  |  |  |  | =head2 _items_counter | 
| 1981 |  |  |  |  |  |  |  | 
| 1982 |  |  |  |  |  |  | Returns the current position in the record set. | 
| 1983 |  |  |  |  |  |  |  | 
| 1984 |  |  |  |  |  |  | =cut | 
| 1985 |  |  |  |  |  |  |  | 
| 1986 |  |  |  |  |  |  | sub _items_counter { | 
| 1987 | 10 |  |  | 10 |  | 15 | my $self = shift; | 
| 1988 | 10 |  |  |  |  | 38 | return $self->{'itemscount'}; | 
| 1989 |  |  |  |  |  |  | } | 
| 1990 |  |  |  |  |  |  |  | 
| 1991 |  |  |  |  |  |  | =head2 count | 
| 1992 |  |  |  |  |  |  |  | 
| 1993 |  |  |  |  |  |  | Returns the number of records in the set. | 
| 1994 |  |  |  |  |  |  |  | 
| 1995 |  |  |  |  |  |  | =cut | 
| 1996 |  |  |  |  |  |  |  | 
| 1997 |  |  |  |  |  |  | sub count { | 
| 1998 | 119 |  |  | 119 | 1 | 1850 | my $self = shift; | 
| 1999 |  |  |  |  |  |  |  | 
| 2000 |  |  |  |  |  |  | # An unlimited search returns no tickets | 
| 2001 | 119 | 100 |  |  |  | 397 | return 0 unless ( $self->_is_limited ); | 
| 2002 |  |  |  |  |  |  |  | 
| 2003 |  |  |  |  |  |  | # If we haven't actually got all objects loaded in memory, we | 
| 2004 |  |  |  |  |  |  | # really just want to do a quick count from the database. | 
| 2005 | 106 | 100 |  |  |  | 1069 | if ( $self->{'must_redo_search'} ) { | 
| 2006 |  |  |  |  |  |  |  | 
| 2007 |  |  |  |  |  |  | # If we haven't already asked the database for the row count, do that | 
| 2008 | 82 | 100 |  |  |  | 438 | $self->_do_count unless ( $self->{'raw_rows'} ); | 
| 2009 |  |  |  |  |  |  |  | 
| 2010 |  |  |  |  |  |  | #Report back the raw # of rows in the database | 
| 2011 | 82 |  |  |  |  | 1066 | return ( $self->{'raw_rows'} ); | 
| 2012 |  |  |  |  |  |  | } | 
| 2013 |  |  |  |  |  |  |  | 
| 2014 |  |  |  |  |  |  | # If we have loaded everything from the DB we have an | 
| 2015 |  |  |  |  |  |  | # accurate count already. | 
| 2016 |  |  |  |  |  |  | else { | 
| 2017 | 24 |  |  |  |  | 66 | return $self->_record_count; | 
| 2018 |  |  |  |  |  |  | } | 
| 2019 |  |  |  |  |  |  | } | 
| 2020 |  |  |  |  |  |  |  | 
| 2021 |  |  |  |  |  |  | =head2 count_all | 
| 2022 |  |  |  |  |  |  |  | 
| 2023 |  |  |  |  |  |  | Returns the total number of potential records in the set, ignoring any | 
| 2024 |  |  |  |  |  |  | limit_clause. | 
| 2025 |  |  |  |  |  |  |  | 
| 2026 |  |  |  |  |  |  | =cut | 
| 2027 |  |  |  |  |  |  |  | 
| 2028 |  |  |  |  |  |  | # 22:24 [Robrt(500@outer.space)] It has to do with Caching. | 
| 2029 |  |  |  |  |  |  | # 22:25 [Robrt(500@outer.space)] The documentation says it ignores the limit. | 
| 2030 |  |  |  |  |  |  | # 22:25 [Robrt(500@outer.space)] But I don't believe thats true. | 
| 2031 |  |  |  |  |  |  | # 22:26 [msg(Robrt)] yeah. I | 
| 2032 |  |  |  |  |  |  | # 22:26 [msg(Robrt)] yeah. I'm not convinced it does anything useful right now | 
| 2033 |  |  |  |  |  |  | # 22:26 [msg(Robrt)] especially since until a week ago, it was setting one variable and returning another | 
| 2034 |  |  |  |  |  |  | # 22:27 [Robrt(500@outer.space)] I remember. | 
| 2035 |  |  |  |  |  |  | # 22:27 [Robrt(500@outer.space)] It had to do with which Cached value was returned. | 
| 2036 |  |  |  |  |  |  | # 22:27 [msg(Robrt)] (given that every time we try to explain it, we get it Wrong) | 
| 2037 |  |  |  |  |  |  | # 22:27 [Robrt(500@outer.space)] Because Count can return a different number than actual NumberOfResults | 
| 2038 |  |  |  |  |  |  | # 22:28 [msg(Robrt)] in what case? | 
| 2039 |  |  |  |  |  |  | # 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 | 
| 2040 |  |  |  |  |  |  | #           results returned. | 
| 2041 |  |  |  |  |  |  | # 22:28 [Robrt(500@outer.space)] IIRC, if you do a search with a limit, then raw_rows will == limit. | 
| 2042 |  |  |  |  |  |  | # 22:31 [msg(Robrt)] ah. | 
| 2043 |  |  |  |  |  |  | # 22:31 [msg(Robrt)] that actually makes sense | 
| 2044 |  |  |  |  |  |  | # 22:31 [Robrt(500@outer.space)] You should paste this conversation into the count_all docs. | 
| 2045 |  |  |  |  |  |  | # 22:31 [msg(Robrt)] perhaps I'll create a new method that _actually_ do that. | 
| 2046 |  |  |  |  |  |  | # 22:32 [msg(Robrt)] since I'm not convinced it's been doing that correctly | 
| 2047 |  |  |  |  |  |  |  | 
| 2048 |  |  |  |  |  |  | sub count_all { | 
| 2049 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 2050 |  |  |  |  |  |  |  | 
| 2051 |  |  |  |  |  |  | # An unlimited search returns no tickets | 
| 2052 | 0 | 0 |  |  |  | 0 | return 0 unless ( $self->_is_limited ); | 
| 2053 |  |  |  |  |  |  |  | 
| 2054 |  |  |  |  |  |  | # If we haven't actually got all objects loaded in memory, we | 
| 2055 |  |  |  |  |  |  | # really just want to do a quick count from the database. | 
| 2056 | 0 | 0 | 0 |  |  | 0 | if ( $self->{'must_redo_search'} || !$self->{'count_all'} ) { | 
| 2057 |  |  |  |  |  |  |  | 
| 2058 |  |  |  |  |  |  | # If we haven't already asked the database for the row count, do that | 
| 2059 | 0 | 0 |  |  |  | 0 | $self->_do_count(1) unless ( $self->{'count_all'} ); | 
| 2060 |  |  |  |  |  |  |  | 
| 2061 |  |  |  |  |  |  | #Report back the raw # of rows in the database | 
| 2062 | 0 |  |  |  |  | 0 | return ( $self->{'count_all'} ); | 
| 2063 |  |  |  |  |  |  | } | 
| 2064 |  |  |  |  |  |  |  | 
| 2065 |  |  |  |  |  |  | # If we have loaded everything from the DB we have an | 
| 2066 |  |  |  |  |  |  | # accurate count already. | 
| 2067 |  |  |  |  |  |  | else { | 
| 2068 | 0 |  |  |  |  | 0 | return $self->_record_count; | 
| 2069 |  |  |  |  |  |  | } | 
| 2070 |  |  |  |  |  |  | } | 
| 2071 |  |  |  |  |  |  |  | 
| 2072 |  |  |  |  |  |  | =head2 is_last | 
| 2073 |  |  |  |  |  |  |  | 
| 2074 |  |  |  |  |  |  | Returns true if the current row is the last record in the set. | 
| 2075 |  |  |  |  |  |  |  | 
| 2076 |  |  |  |  |  |  | =cut | 
| 2077 |  |  |  |  |  |  |  | 
| 2078 |  |  |  |  |  |  | sub is_last { | 
| 2079 | 18 |  |  | 18 | 1 | 10391 | my $self = shift; | 
| 2080 |  |  |  |  |  |  |  | 
| 2081 | 18 | 100 |  |  |  | 101 | return undef unless $self->count; | 
| 2082 |  |  |  |  |  |  |  | 
| 2083 | 10 | 100 |  |  |  | 51 | if ( $self->_items_counter == $self->count ) { | 
| 2084 | 6 |  |  |  |  | 41 | return (1); | 
| 2085 |  |  |  |  |  |  | } else { | 
| 2086 | 4 |  |  |  |  | 21 | return (0); | 
| 2087 |  |  |  |  |  |  | } | 
| 2088 |  |  |  |  |  |  | } | 
| 2089 |  |  |  |  |  |  |  | 
| 2090 |  |  |  |  |  |  | =head2 DEBUG | 
| 2091 |  |  |  |  |  |  |  | 
| 2092 |  |  |  |  |  |  | Gets/sets the DEBUG flag. | 
| 2093 |  |  |  |  |  |  |  | 
| 2094 |  |  |  |  |  |  | =cut | 
| 2095 |  |  |  |  |  |  |  | 
| 2096 |  |  |  |  |  |  | sub DEBUG { | 
| 2097 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 2098 | 0 | 0 |  |  |  | 0 | if (@_) { | 
| 2099 | 0 |  |  |  |  | 0 | $self->{'DEBUG'} = shift; | 
| 2100 |  |  |  |  |  |  | } | 
| 2101 | 0 |  |  |  |  | 0 | return ( $self->{'DEBUG'} ); | 
| 2102 |  |  |  |  |  |  | } | 
| 2103 |  |  |  |  |  |  |  | 
| 2104 |  |  |  |  |  |  | =head2 column | 
| 2105 |  |  |  |  |  |  |  | 
| 2106 |  |  |  |  |  |  | Normally a collection object contains record objects populated with all columns | 
| 2107 |  |  |  |  |  |  | in the database, but you can restrict the records to only contain some | 
| 2108 |  |  |  |  |  |  | particular columns, by calling the C method once for each column you | 
| 2109 |  |  |  |  |  |  | are interested in. | 
| 2110 |  |  |  |  |  |  |  | 
| 2111 |  |  |  |  |  |  | Takes a hash of parameters; the C, C  and C keys means 
| 2112 |  |  |  |  |  |  | the same as in the C method.  A special C key may contain |  
| 2113 |  |  |  |  |  |  | one of several possible kinds of expressions: |  
| 2114 |  |  |  |  |  |  |  |  
| 2115 |  |  |  |  |  |  | =over 4 |  
| 2116 |  |  |  |  |  |  |  |  
| 2117 |  |  |  |  |  |  | =item C |  
| 2118 |  |  |  |  |  |  |  |  
| 2119 |  |  |  |  |  |  | Same as C. |  
| 2120 |  |  |  |  |  |  |  |  
| 2121 |  |  |  |  |  |  | =item Expression with C> in it |  
| 2122 |  |  |  |  |  |  |  |  
| 2123 |  |  |  |  |  |  | The C> is substituted with the column name, then passed verbatim to the |  
| 2124 |  |  |  |  |  |  | underlying C statement. |  
| 2125 |  |  |  |  |  |  |  |  
| 2126 |  |  |  |  |  |  | =item Expression with C<(> in it |  
| 2127 |  |  |  |  |  |  |  |  
| 2128 |  |  |  |  |  |  | The expression is passed verbatim to the underlying C. |  
| 2129 |  |  |  |  |  |  |  |  
| 2130 |  |  |  |  |  |  | =item Any other expression |  
| 2131 |  |  |  |  |  |  |  |  
| 2132 |  |  |  |  |  |  | The expression is taken to be a function name.  For example, C means |  
| 2133 |  |  |  |  |  |  | the same thing as C. |  
| 2134 |  |  |  |  |  |  |  |  
| 2135 |  |  |  |  |  |  | =back |  
| 2136 |  |  |  |  |  |  |  |  
| 2137 |  |  |  |  |  |  | =cut |  
| 2138 |  |  |  |  |  |  |  |  
| 2139 |  |  |  |  |  |  | sub column { |  
| 2140 | 3 |  |  | 3 | 1 | 34 | my $self = shift; |  
| 2141 | 3 |  |  |  |  | 41 | my %args = ( |  
| 2142 |  |  |  |  |  |  | table    => undef, |  
| 2143 |  |  |  |  |  |  | alias    => undef, |  
| 2144 |  |  |  |  |  |  | column   => undef, |  
| 2145 |  |  |  |  |  |  | function => undef, |  
| 2146 |  |  |  |  |  |  | @_ |  
| 2147 |  |  |  |  |  |  | ); |  
| 2148 |  |  |  |  |  |  |  |  
| 2149 | 3 |  | 33 |  |  | 25 | my $table = $args{table} || do { |  
| 2150 |  |  |  |  |  |  | if ( my $alias = $args{alias} ) { |  
| 2151 |  |  |  |  |  |  | $alias =~ s/_\d+$//; |  
| 2152 |  |  |  |  |  |  | $alias; |  
| 2153 |  |  |  |  |  |  | } else { |  
| 2154 |  |  |  |  |  |  | $self->table; |  
| 2155 |  |  |  |  |  |  | } |  
| 2156 |  |  |  |  |  |  | }; |  
| 2157 |  |  |  |  |  |  |  |  
| 2158 | 3 |  | 50 |  |  | 29 | my $name = ( $args{alias} || 'main' ) . '.' . $args{column}; |  
| 2159 | 3 | 50 |  |  |  | 14 | if ( my $func = $args{function} ) { |  
| 2160 | 0 | 0 |  |  |  | 0 | if ( $func =~ /^DISTINCT\s*COUNT$/i ) { |  
|  |  | 0 |  |  |  |  |  |  
|  |  | 0 |  |  |  |  |  |  
| 2161 | 0 |  |  |  |  | 0 | $name = "COUNT(DISTINCT $name)"; |  
| 2162 |  |  |  |  |  |  | } |  
| 2163 |  |  |  |  |  |  |  |  
| 2164 |  |  |  |  |  |  | # If we want to substitute |  
| 2165 |  |  |  |  |  |  | elsif ( $func =~ /\?/ ) { |  
| 2166 | 0 |  |  |  |  | 0 | $name =~ s/\?/$name/g; |  
| 2167 |  |  |  |  |  |  | } |  
| 2168 |  |  |  |  |  |  |  |  
| 2169 |  |  |  |  |  |  | # If we want to call a simple function on the column |  
| 2170 |  |  |  |  |  |  | elsif ( $func !~ /\(/ ) { |  
| 2171 | 0 |  |  |  |  | 0 | $name = "\U$func\E($name)"; |  
| 2172 |  |  |  |  |  |  | } else { |  
| 2173 | 0 |  |  |  |  | 0 | $name = $func; |  
| 2174 |  |  |  |  |  |  | } |  
| 2175 |  |  |  |  |  |  |  |  
| 2176 |  |  |  |  |  |  | } |  
| 2177 |  |  |  |  |  |  |  |  
| 2178 | 3 |  | 50 |  |  | 8 | my $column = "col" . @{ $self->{columns} ||= [] }; |  
|  | 3 |  |  |  |  | 31 |  |  
| 2179 | 3 | 50 | 33 |  |  | 34 | $column = $args{column} if $table eq $self->table and !$args{alias}; |  
| 2180 | 3 |  | 50 |  |  | 33 | $column = ( $args{'alias'} || 'main' ) . "_" . $column; |  
| 2181 | 3 |  |  |  |  | 8 | push @{ $self->{columns} }, "$name AS \L$column"; |  
|  | 3 |  |  |  |  | 19 |  |  
| 2182 | 3 |  |  |  |  | 16 | return $column; |  
| 2183 |  |  |  |  |  |  | } |  
| 2184 |  |  |  |  |  |  |  |  
| 2185 |  |  |  |  |  |  | =head2 columns LIST |  
| 2186 |  |  |  |  |  |  |  |  
| 2187 |  |  |  |  |  |  | Specify that we want to load only the columns in LIST, which should be |  
| 2188 |  |  |  |  |  |  | a list of column names. |  
| 2189 |  |  |  |  |  |  |  |  
| 2190 |  |  |  |  |  |  | =cut |  
| 2191 |  |  |  |  |  |  |  |  
| 2192 |  |  |  |  |  |  | sub columns { |  
| 2193 | 1 |  |  | 1 | 1 | 15 | my $self = shift; |  
| 2194 | 1 |  |  |  |  | 10 | $self->column( column => $_ ) for @_; |  
| 2195 |  |  |  |  |  |  | } |  
| 2196 |  |  |  |  |  |  |  |  
| 2197 |  |  |  |  |  |  | =head2 columns_in_db table |  
| 2198 |  |  |  |  |  |  |  |  
| 2199 |  |  |  |  |  |  | Return a list of columns in table, in lowercase. |  
| 2200 |  |  |  |  |  |  |  |  
| 2201 |  |  |  |  |  |  | TODO: Why are they in lowercase? |  
| 2202 |  |  |  |  |  |  |  |  
| 2203 |  |  |  |  |  |  | =cut |  
| 2204 |  |  |  |  |  |  |  |  
| 2205 |  |  |  |  |  |  | sub columns_in_db { |  
| 2206 | 0 |  |  | 0 | 1 | 0 | my $self  = shift; |  
| 2207 | 0 |  |  |  |  | 0 | my $table = shift; |  
| 2208 |  |  |  |  |  |  |  |  
| 2209 | 0 |  |  |  |  | 0 | my $dbh = $self->_handle->dbh; |  
| 2210 |  |  |  |  |  |  |  |  
| 2211 |  |  |  |  |  |  | # TODO: memoize this |  
| 2212 |  |  |  |  |  |  |  |  
| 2213 | 0 |  |  |  |  | 0 | return map lc( $_->[0] ), @{ ( |  
| 2214 | 0 | 0 | 0 |  |  | 0 | eval { |  
|  |  |  | 0 |  |  |  |  |  
| 2215 | 0 |  |  |  |  | 0 | $dbh->column_info( '', '', $table, '' )->fetchall_arrayref( [3] ); |  
| 2216 |  |  |  |  |  |  | } |  
| 2217 |  |  |  |  |  |  | || $dbh->selectall_arrayref("DESCRIBE $table;") |  
| 2218 |  |  |  |  |  |  | || $dbh->selectall_arrayref("DESCRIBE \u$table;") |  
| 2219 |  |  |  |  |  |  | || [] |  
| 2220 |  |  |  |  |  |  | ) }; |  
| 2221 |  |  |  |  |  |  | } |  
| 2222 |  |  |  |  |  |  |  |  
| 2223 |  |  |  |  |  |  | =head2 has_column  { table => undef, column => undef } |  
| 2224 |  |  |  |  |  |  |  |  
| 2225 |  |  |  |  |  |  | Returns true if table has column column. |  
| 2226 |  |  |  |  |  |  | Return false otherwise |  
| 2227 |  |  |  |  |  |  |  |  
| 2228 |  |  |  |  |  |  | =cut |  
| 2229 |  |  |  |  |  |  |  |  
| 2230 |  |  |  |  |  |  | sub has_column { |  
| 2231 | 0 |  |  | 0 | 1 | 0 | my $self = shift; |  
| 2232 | 0 |  |  |  |  | 0 | my %args = ( |  
| 2233 |  |  |  |  |  |  | column => undef, |  
| 2234 |  |  |  |  |  |  | table  => undef, |  
| 2235 |  |  |  |  |  |  | @_ |  
| 2236 |  |  |  |  |  |  | ); |  
| 2237 |  |  |  |  |  |  |  |  
| 2238 | 0 | 0 |  |  |  | 0 | my $table  = $args{table}  or die; |  
| 2239 | 0 | 0 |  |  |  | 0 | my $column = $args{column} or die; |  
| 2240 | 0 |  |  |  |  | 0 | return grep { $_ eq $column } $self->columns_in_db($table); |  
|  | 0 |  |  |  |  | 0 |  |  
| 2241 |  |  |  |  |  |  | } |  
| 2242 |  |  |  |  |  |  |  |  
| 2243 |  |  |  |  |  |  | =head2 table [table] |  
| 2244 |  |  |  |  |  |  |  |  
| 2245 |  |  |  |  |  |  | If called with an argument, sets this collection's table. |  
| 2246 |  |  |  |  |  |  |  |  
| 2247 |  |  |  |  |  |  | Always returns this collection's table. |  
| 2248 |  |  |  |  |  |  |  |  
| 2249 |  |  |  |  |  |  | =cut |  
| 2250 |  |  |  |  |  |  |  |  
| 2251 |  |  |  |  |  |  | sub table { |  
| 2252 | 559 |  |  | 559 | 1 | 2437 | my $self = shift; |  
| 2253 | 559 | 100 |  |  |  | 2336 | $self->{table} = shift if (@_); |  
| 2254 | 559 |  |  |  |  | 2342 | return $self->{table}; |  
| 2255 |  |  |  |  |  |  | } |  
| 2256 |  |  |  |  |  |  |  |  
| 2257 |  |  |  |  |  |  | =head2 clone |  
| 2258 |  |  |  |  |  |  |  |  
| 2259 |  |  |  |  |  |  | Returns copy of the current object with all search restrictions. |  
| 2260 |  |  |  |  |  |  |  |  
| 2261 |  |  |  |  |  |  | =cut |  
| 2262 |  |  |  |  |  |  |  |  
| 2263 |  |  |  |  |  |  | sub clone { |  
| 2264 | 2 |  |  | 2 | 1 | 36 | my $self = shift; |  
| 2265 |  |  |  |  |  |  |  |  
| 2266 | 2 |  |  |  |  | 12 | my $obj = bless {}, ref($self); |  
| 2267 | 2 |  |  |  |  | 42 | %$obj = %$self; |  
| 2268 |  |  |  |  |  |  |  |  
| 2269 | 2 |  |  |  |  | 17 | $obj->redo_search();    # clean out the object of data |  
| 2270 |  |  |  |  |  |  |  |  
| 2271 |  |  |  |  |  |  | $obj->{$_} = Clone::clone( $obj->{$_} ) |  
| 2272 | 2 |  |  |  |  | 23 | for grep exists $self->{$_}, $self->_cloned_attributes; |  
| 2273 | 2 |  |  |  |  | 12 | return $obj; |  
| 2274 |  |  |  |  |  |  | } |  
| 2275 |  |  |  |  |  |  |  |  
| 2276 |  |  |  |  |  |  | =head2 _cloned_attributes |  
| 2277 |  |  |  |  |  |  |  |  
| 2278 |  |  |  |  |  |  | Returns list of the object's fields that should be copied. |  
| 2279 |  |  |  |  |  |  |  |  
| 2280 |  |  |  |  |  |  | If your subclass store references in the object that should be copied while |  
| 2281 |  |  |  |  |  |  | cloning then you probably want override this method and add own values to |  
| 2282 |  |  |  |  |  |  | the list. |  
| 2283 |  |  |  |  |  |  |  |  
| 2284 |  |  |  |  |  |  | =cut |  
| 2285 |  |  |  |  |  |  |  |  
| 2286 |  |  |  |  |  |  | sub _cloned_attributes { |  
| 2287 | 2 |  |  | 2 |  | 24 | return qw( |  
| 2288 |  |  |  |  |  |  | joins |  
| 2289 |  |  |  |  |  |  | subclauses |  
| 2290 |  |  |  |  |  |  | restrictions |  
| 2291 |  |  |  |  |  |  | ); |  
| 2292 |  |  |  |  |  |  | } |  
| 2293 |  |  |  |  |  |  |  |  
| 2294 |  |  |  |  |  |  | =head2 each CALLBACK |  
| 2295 |  |  |  |  |  |  |  |  
| 2296 |  |  |  |  |  |  | Executes the callback for each item in the collection. The callback receives as |  
| 2297 |  |  |  |  |  |  | arguments each record, its zero-based index, and the collection. The return |  
| 2298 |  |  |  |  |  |  | value of C is the original collection. |  
| 2299 |  |  |  |  |  |  |  |  
| 2300 |  |  |  |  |  |  | If the callback returns zero, the iteration ends. |  
| 2301 |  |  |  |  |  |  |  |  
| 2302 |  |  |  |  |  |  | =cut |  
| 2303 |  |  |  |  |  |  |  |  
| 2304 |  |  |  |  |  |  | sub each { |  
| 2305 | 0 |  |  | 0 | 1 |  | my $self = shift; |  
| 2306 | 0 |  |  |  |  |  | my $cb   = shift; |  
| 2307 |  |  |  |  |  |  |  |  
| 2308 | 0 |  |  |  |  |  | my $idx = 0; |  
| 2309 | 0 |  |  |  |  |  | $self->goto_first_item; |  
| 2310 |  |  |  |  |  |  |  |  
| 2311 | 0 |  |  |  |  |  | while (my $record = $self->next) { |  
| 2312 | 0 |  |  |  |  |  | my $ret = $cb->($record, $idx++, $self); |  
| 2313 | 0 | 0 | 0 |  |  |  | last if defined($ret) && !$ret; |  
| 2314 |  |  |  |  |  |  | } |  
| 2315 |  |  |  |  |  |  |  |  
| 2316 | 0 |  |  |  |  |  | return $self; |  
| 2317 |  |  |  |  |  |  | } |  
| 2318 |  |  |  |  |  |  |  |  
| 2319 |  |  |  |  |  |  | 1; |  
| 2320 |  |  |  |  |  |  | __END__ |  |