* Added ->first and ->reset implementation to ResultSetColumn.
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSetColumn.pm
1 package DBIx::Class::ResultSetColumn;
2 use strict;
3 use warnings;
4 use base 'DBIx::Class';
5 use List::Util;
6
7 =head1 NAME
8
9   DBIx::Class::ResultSetColumn - helpful methods for messing
10   with a single column of the resultset
11
12 =head1 SYNOPSIS
13
14   $rs = $schema->resultset('CD')->search({ artist => 'Tool' });
15   $rs_column = $rs->get_column('year');
16   $max_year = $rs_column->max; #returns latest year
17
18 =head1 DESCRIPTION
19
20 A convenience class used to perform operations on a specific column of
21 a resultset.
22
23 =cut
24
25 =head1 METHODS
26
27 =head2 new
28
29   my $obj = DBIx::Class::ResultSetColumn->new($rs, $column);
30
31 Creates a new resultset column object from the resultset and column
32 passed as params. Used internally by L<DBIx::Class::ResultSet/get_column>.
33
34 =cut
35
36 sub new {
37   my ($class, $rs, $column) = @_;
38   $class = ref $class if ref $class;
39   my $new_parent_rs = $rs->search_rs; # we don't want to mess up the original, so clone it
40   my $attrs = $new_parent_rs->_resolved_attrs;
41   $new_parent_rs->{attrs}->{$_} = undef for qw(prefetch include_columns +select +as); # prefetch, include_columns, +select, +as cause additional columns to be fetched
42   my ($select, $as) =
43     map { defined $_ ? ($attrs->{select}->[$_], $attrs->{as}->[$_]) : ($column, $column) }
44       List::Util::first { ($attrs->{as} || [])->[$_] eq $column }
45         0..$#{$attrs->{as} || []};
46   my $new = bless { _select => $select, _as => $as, _parent_resultset => $new_parent_rs }, $class;
47   $new->throw_exception("column must be supplied") unless $column;
48   return $new;
49 }
50
51 =head2 next
52
53 =over 4
54
55 =item Arguments: none
56
57 =item Return Value: $value
58
59 =back
60
61 Returns the next value of the column in the resultset (or C<undef> if
62 there is none).
63
64 Much like L<DBIx::Class::ResultSet/next> but just returning the 
65 one value.
66
67 =cut
68
69 sub next {
70   my $self = shift;
71   my ($row) = $self->_resultset->cursor->next;
72   return $row;
73 }
74
75 =head2 all
76
77 =over 4
78
79 =item Arguments: none
80
81 =item Return Value: @values
82
83 =back
84
85 Returns all values of the column in the resultset (or C<undef> if
86 there are none).
87
88 Much like L<DBIx::Class::ResultSet/all> but returns values rather
89 than row objects.
90
91 =cut
92
93 sub all {
94   my $self = shift;
95   return map { $_->[0] } $self->_resultset->cursor->all;
96 }
97
98 =head2 reset
99
100 =over 4
101
102 =item Arguments: none
103
104 =item Return Value: $self
105
106 =back
107
108 Resets the underlying resultset's cursor, so you can iterate through the
109 elements of the column again.
110
111 Much like L<DBIx::Class::ResultSet/reset>.
112
113 =cut
114
115 sub reset {
116   my $self = shift;
117   $self->_resultset->cursor->reset;
118   return $self;
119 }
120
121 =head2 first
122
123 =over 4
124
125 =item Arguments: none
126
127 =item Return Value: $value
128
129 =back
130
131 Resets the underlying resultset and returns the next value of the column in the
132 resultset (or C<undef> if there is none).
133
134 Much like L<DBIx::Class::ResultSet/first> but just returning the one value.
135
136 =cut
137
138 sub first {
139   my $self = shift;
140   my ($row) = $self->{_resultset}->cursor->reset->next;
141   return $row;
142 }
143
144 =head2 min
145
146 =over 4
147
148 =item Arguments: none
149
150 =item Return Value: $lowest_value
151
152 =back
153
154   my $first_year = $year_col->min();
155
156 Wrapper for ->func. Returns the lowest value of the column in the
157 resultset (or C<undef> if there are none).
158
159 =cut
160
161 sub min {
162   return shift->func('MIN');
163 }
164
165 =head2 max
166
167 =over 4
168
169 =item Arguments: none
170
171 =item Return Value: $highest_value
172
173 =back
174
175   my $last_year = $year_col->max();
176
177 Wrapper for ->func. Returns the highest value of the column in the
178 resultset (or C<undef> if there are none).
179
180 =cut
181
182 sub max {
183   return shift->func('MAX');
184 }
185
186 =head2 sum
187
188 =over 4
189
190 =item Arguments: none
191
192 =item Return Value: $sum_of_values
193
194 =back
195
196   my $total = $prices_col->sum();
197
198 Wrapper for ->func. Returns the sum of all the values in the column of
199 the resultset. Use on varchar-like columns at your own risk.
200
201 =cut
202
203 sub sum {
204   return shift->func('SUM');
205 }
206
207 =head2 func
208
209 =over 4
210
211 =item Arguments: $function
212
213 =item Return Value: $function_return_value
214
215 =back
216
217   $rs = $schema->resultset("CD")->search({});
218   $length = $rs->get_column('title')->func('LENGTH');
219
220 Runs a query using the function on the column and returns the
221 value. Produces the following SQL:
222
223   SELECT LENGTH( title ) FROM cd me
224
225 =cut
226
227 sub func {
228   my ($self,$function) = @_;
229   my $cursor = $self->{_parent_resultset}->search(undef, {select => {$function => $self->{_select}}, as => [$self->{_as}]})->cursor;
230   
231   if( wantarray ) {
232     return map { $_->[ 0 ] } $cursor->all;
233   }
234
235   return ( $cursor->next )[ 0 ];
236 }
237
238 =head2 throw_exception
239
240 See L<DBIx::Class::Schema/throw_exception> for details.
241   
242 =cut 
243     
244 sub throw_exception {
245   my $self=shift;
246   if (ref $self && $self->{_parent_resultset}) {
247     $self->{_parent_resultset}->throw_exception(@_)
248   } else {
249     croak(@_);
250   }
251 }
252
253 =head2 _resultset
254
255 =over 4
256
257 =item Arguments: none
258
259 =item Return Value: $resultset
260
261 =back
262
263   $year_col->_resultset->next
264
265 Returns the underlying resultset. Creates it from the parent resultset if
266 necessary.
267
268 =cut
269
270 sub _resultset {
271   my $self = shift;
272
273   return $self->{_resultset} ||= $self->{_parent_resultset}->search(undef,
274     {
275       select => [$self->{_select}],
276       as => [$self->{_as}]
277     }
278   );
279 }
280
281
282 1;
283
284 =head1 AUTHORS
285
286 Luke Saunders <luke.saunders@gmail.com>
287
288 Jess Robinson
289
290 =head1 LICENSE
291
292 You may distribute this code under the same terms as Perl itself.
293
294 =cut