AUTHORS mass update; mst doesn't have to take credit for -everything- :)
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSetColumn.pm
CommitLineData
2bb7b40b 1package DBIx::Class::ResultSetColumn;
1a58752c 2
2bb7b40b 3use strict;
4use warnings;
1a58752c 5
2bb7b40b 6use base 'DBIx::Class';
70c28808 7use DBIx::Class::Carp;
1a58752c 8use DBIx::Class::Exception;
94244987 9
10# not importing first() as it will clash with our own method
6298a324 11use List::Util ();
2bb7b40b 12
13=head1 NAME
14
15 DBIx::Class::ResultSetColumn - helpful methods for messing
16 with a single column of the resultset
17
18=head1 SYNOPSIS
19
20 $rs = $schema->resultset('CD')->search({ artist => 'Tool' });
21 $rs_column = $rs->get_column('year');
22 $max_year = $rs_column->max; #returns latest year
23
24=head1 DESCRIPTION
25
eb98561c 26A convenience class used to perform operations on a specific column of
27a resultset.
2bb7b40b 28
29=cut
30
31=head1 METHODS
32
33=head2 new
34
35 my $obj = DBIx::Class::ResultSetColumn->new($rs, $column);
36
eb98561c 37Creates a new resultset column object from the resultset and column
38passed as params. Used internally by L<DBIx::Class::ResultSet/get_column>.
2bb7b40b 39
40=cut
41
42sub new {
43 my ($class, $rs, $column) = @_;
44 $class = ref $class if ref $class;
7ae9706c 45
66e33361 46 $rs->throw_exception('column must be supplied') unless $column;
7ae9706c 47
d8dbe471 48 my $orig_attrs = $rs->_resolved_attrs;
9b8930f4 49 my $alias = $rs->current_source_alias;
2b3fc408 50 my $rsrc = $rs->result_source;
472d7df3 51
52 # If $column can be found in the 'as' list of the parent resultset, use the
53 # corresponding element of its 'select' list (to keep any custom column
54 # definition set up with 'select' or '+select' attrs), otherwise use $column
55 # (to create a new column definition on-the-fly).
56 my $as_list = $orig_attrs->{as} || [];
57 my $select_list = $orig_attrs->{select} || [];
58 my $as_index = List::Util::first { ($as_list->[$_] || "") eq $column } 0..$#$as_list;
59 my $select = defined $as_index ? $select_list->[$as_index] : $column;
60
2b3fc408 61 my ($new_parent_rs, $colmap);
62 for ($rsrc->columns, $column) {
63 if ($_ =~ /^ \Q$alias\E \. ([^\.]+) $ /x) {
64 $colmap->{$_} = $1;
65 }
66 elsif ($_ !~ /\./) {
67 $colmap->{"$alias.$_"} = $_;
68 $colmap->{$_} = $_;
69 }
70 }
71
472d7df3 72 # analyze the order_by, and see if it is done over a function/nonexistentcolumn
73 # if this is the case we will need to wrap a subquery since the result of RSC
74 # *must* be a single column select
472d7df3 75 if (
76 scalar grep
2b3fc408 77 { ! exists $colmap->{$_->[0]} }
78 ( $rsrc->schema->storage->_extract_order_criteria ($orig_attrs->{order_by} ) )
472d7df3 79 ) {
5716e003 80 # nuke the prefetch before collapsing to sql
81 my $subq_rs = $rs->search;
37aafa2e 82 $subq_rs->{attrs}{join} = $subq_rs->_merge_joinpref_attr( $subq_rs->{attrs}{join}, delete $subq_rs->{attrs}{prefetch} );
9b8930f4 83 $new_parent_rs = $subq_rs->as_subselect_rs;
472d7df3 84 }
85
86 $new_parent_rs ||= $rs->search_rs;
66e33361 87 my $new_attrs = $new_parent_rs->{attrs} ||= {};
88
7ae9706c 89 # prefetch causes additional columns to be fetched, but we can not just make a new
90 # rs via the _resolved_attrs trick - we need to retain the separation between
bed3a173 91 # +select/+as and select/as. At the same time we want to preserve any joins that the
92 # prefetch would otherwise generate.
37aafa2e 93 $new_attrs->{join} = $rs->_merge_joinpref_attr( $new_attrs->{join}, delete $new_attrs->{prefetch} );
b6e85b48 94
d8dbe471 95 # {collapse} would mean a has_many join was injected, which in turn means
722c0140 96 # we need to group *IF WE CAN* (only if the column in question is unique)
c9733800 97 if (!$orig_attrs->{group_by} && keys %{$orig_attrs->{collapse}}) {
d8dbe471 98
2b3fc408 99 if ($colmap->{$select} and $rsrc->_identifying_column_set([$colmap->{$select}])) {
100 $new_attrs->{group_by} = [ $select ];
101 delete $new_attrs->{distinct}; # it is ignored when group_by is present
d8dbe471 102 }
2b3fc408 103 else {
722c0140 104 carp (
105 "Attempting to retrieve non-unique column '$column' on a resultset containing "
106 . 'one-to-many joins will return duplicate results.'
107 );
108 }
d8dbe471 109 }
110
b6e85b48 111 my $new = bless { _select => $select, _as => $column, _parent_resultset => $new_parent_rs }, $class;
2bb7b40b 112 return $new;
113}
114
6dfbe2f8 115=head2 as_query
658fa250 116
117=over 4
118
428a645e 119=item Arguments: none
658fa250 120
4dc99a01 121=item Return Value: \[ $sql, @bind ]
658fa250 122
123=back
124
125Returns the SQL query and bind vars associated with the invocant.
126
03834f77 127This is generally used as the RHS for a subquery.
c7a9d102 128
129=cut
130
0f6fc705 131sub as_query { return shift->_resultset->as_query(@_) }
c7a9d102 132
2bb7b40b 133=head2 next
134
135=over 4
136
137=item Arguments: none
138
139=item Return Value: $value
140
141=back
142
eb98561c 143Returns the next value of the column in the resultset (or C<undef> if
144there is none).
2bb7b40b 145
8273e845 146Much like L<DBIx::Class::ResultSet/next> but just returning the
eb98561c 147one value.
2bb7b40b 148
149=cut
150
151sub next {
152 my $self = shift;
b7c79955 153
154 # using cursor so we don't inflate anything
66521001 155 my ($row) = $self->_resultset->cursor->next;
b7c79955 156
2bb7b40b 157 return $row;
158}
159
160=head2 all
161
162=over 4
163
164=item Arguments: none
165
166=item Return Value: @values
167
168=back
169
eb98561c 170Returns all values of the column in the resultset (or C<undef> if
171there are none).
2bb7b40b 172
eb98561c 173Much like L<DBIx::Class::ResultSet/all> but returns values rather
174than row objects.
2bb7b40b 175
176=cut
177
178sub all {
179 my $self = shift;
b7c79955 180
181 # using cursor so we don't inflate anything
66521001 182 return map { $_->[0] } $self->_resultset->cursor->all;
183}
184
185=head2 reset
186
187=over 4
188
189=item Arguments: none
190
191=item Return Value: $self
192
193=back
194
195Resets the underlying resultset's cursor, so you can iterate through the
196elements of the column again.
197
198Much like L<DBIx::Class::ResultSet/reset>.
199
200=cut
201
202sub reset {
203 my $self = shift;
204 $self->_resultset->cursor->reset;
b7c79955 205 return $self;
66521001 206}
207
208=head2 first
209
210=over 4
211
212=item Arguments: none
213
214=item Return Value: $value
215
216=back
217
218Resets the underlying resultset and returns the next value of the column in the
219resultset (or C<undef> if there is none).
220
221Much like L<DBIx::Class::ResultSet/first> but just returning the one value.
222
223=cut
224
225sub first {
226 my $self = shift;
b7c79955 227
228 # using cursor so we don't inflate anything
229 $self->_resultset->cursor->reset;
01dc6781 230 my ($row) = $self->_resultset->cursor->next;
b7c79955 231
66521001 232 return $row;
2bb7b40b 233}
234
4e55c3ae 235=head2 single
236
237=over 4
238
239=item Arguments: none
240
241=item Return Value: $value
242
243=back
244
245Much like L<DBIx::Class::ResultSet/single> fetches one and only one column
246value using the cursor directly. If additional rows are present a warning
247is issued before discarding the cursor.
248
249=cut
250
251sub single {
252 my $self = shift;
253
254 my $attrs = $self->_resultset->_resolved_attrs;
255 my ($row) = $self->_resultset->result_source->storage->select_single(
256 $attrs->{from}, $attrs->{select}, $attrs->{where}, $attrs
257 );
258
259 return $row;
260}
261
2bb7b40b 262=head2 min
263
264=over 4
265
266=item Arguments: none
267
268=item Return Value: $lowest_value
269
270=back
271
eb98561c 272 my $first_year = $year_col->min();
273
274Wrapper for ->func. Returns the lowest value of the column in the
275resultset (or C<undef> if there are none).
2bb7b40b 276
277=cut
278
279sub min {
6b051e14 280 return shift->func('MIN');
2bb7b40b 281}
282
4fa7bc22 283=head2 min_rs
284
285=over 4
286
287=item Arguments: none
288
289=item Return Value: $resultset
290
291=back
292
293 my $rs = $year_col->min_rs();
294
295Wrapper for ->func_rs for function MIN().
296
297=cut
298
299sub min_rs { return shift->func_rs('MIN') }
300
2bb7b40b 301=head2 max
302
303=over 4
304
305=item Arguments: none
306
307=item Return Value: $highest_value
308
309=back
310
eb98561c 311 my $last_year = $year_col->max();
312
313Wrapper for ->func. Returns the highest value of the column in the
314resultset (or C<undef> if there are none).
2bb7b40b 315
316=cut
317
318sub max {
6b051e14 319 return shift->func('MAX');
2bb7b40b 320}
321
4fa7bc22 322=head2 max_rs
323
324=over 4
325
326=item Arguments: none
327
328=item Return Value: $resultset
329
330=back
331
332 my $rs = $year_col->max_rs();
333
334Wrapper for ->func_rs for function MAX().
335
336=cut
337
338sub max_rs { return shift->func_rs('MAX') }
339
2bb7b40b 340=head2 sum
341
342=over 4
343
344=item Arguments: none
345
346=item Return Value: $sum_of_values
347
348=back
349
eb98561c 350 my $total = $prices_col->sum();
351
352Wrapper for ->func. Returns the sum of all the values in the column of
353the resultset. Use on varchar-like columns at your own risk.
2bb7b40b 354
355=cut
356
357sub sum {
6b051e14 358 return shift->func('SUM');
2bb7b40b 359}
360
4fa7bc22 361=head2 sum_rs
362
363=over 4
364
365=item Arguments: none
366
367=item Return Value: $resultset
368
369=back
370
371 my $rs = $year_col->sum_rs();
372
373Wrapper for ->func_rs for function SUM().
374
375=cut
376
377sub sum_rs { return shift->func_rs('SUM') }
378
2bb7b40b 379=head2 func
380
381=over 4
382
383=item Arguments: $function
384
385=item Return Value: $function_return_value
386
387=back
388
e8419341 389 $rs = $schema->resultset("CD")->search({});
390 $length = $rs->get_column('title')->func('LENGTH');
2bb7b40b 391
eb98561c 392Runs a query using the function on the column and returns the
393value. Produces the following SQL:
394
395 SELECT LENGTH( title ) FROM cd me
2bb7b40b 396
397=cut
398
399sub func {
6b051e14 400 my ($self,$function) = @_;
4fa7bc22 401 my $cursor = $self->func_rs($function)->cursor;
d4daee7b 402
5d62876f 403 if( wantarray ) {
404 return map { $_->[ 0 ] } $cursor->all;
405 }
406
407 return ( $cursor->next )[ 0 ];
2bb7b40b 408}
409
4fa7bc22 410=head2 func_rs
411
412=over 4
413
414=item Arguments: $function
415
416=item Return Value: $resultset
417
418=back
419
420Creates the resultset that C<func()> uses to run its query.
421
422=cut
423
424sub func_rs {
425 my ($self,$function) = @_;
426 return $self->{_parent_resultset}->search(
427 undef, {
428 select => {$function => $self->{_select}},
429 as => [$self->{_as}],
430 },
431 );
432}
433
5d1fc7dc 434=head2 throw_exception
435
436See L<DBIx::Class::Schema/throw_exception> for details.
d4daee7b 437
8273e845 438=cut
d4daee7b 439
5d1fc7dc 440sub throw_exception {
441 my $self=shift;
1a58752c 442
5d1fc7dc 443 if (ref $self && $self->{_parent_resultset}) {
1a58752c 444 $self->{_parent_resultset}->throw_exception(@_);
445 }
446 else {
447 DBIx::Class::Exception->throw(@_);
5d1fc7dc 448 }
449}
450
b6e85b48 451# _resultset
452#
453# Arguments: none
454#
455# Return Value: $resultset
456#
457# $year_col->_resultset->next
458#
459# Returns the underlying resultset. Creates it from the parent resultset if
460# necessary.
b7c79955 461#
66521001 462sub _resultset {
463 my $self = shift;
464
465 return $self->{_resultset} ||= $self->{_parent_resultset}->search(undef,
466 {
467 select => [$self->{_select}],
468 as => [$self->{_as}]
469 }
470 );
471}
472
2bb7b40b 4731;
474
0c11ad0e 475=head1 AUTHOR AND CONTRIBUTORS
2bb7b40b 476
0c11ad0e 477See L<AUTHOR|DBIx::Class/AUTHOR> and L<CONTRIBUTORS|DBIx::Class/CONTRIBUTORS> in DBIx::Class
eb98561c 478
2bb7b40b 479=head1 LICENSE
480
481You may distribute this code under the same terms as Perl itself.
482
483=cut