fix mystery tabs in changes file
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / ResultSet.pm
CommitLineData
89c0a5a2 1package DBIx::Class::ResultSet;
2
3use strict;
4use warnings;
5use overload
6 '0+' => 'count',
7 fallback => 1;
3c5b25c5 8use Data::Page;
89c0a5a2 9
ee38fa40 10=head1 NAME
11
4ffcf5e0 12DBIx::Class::ResultSet - Responsible for fetching and creating resultset.
ee38fa40 13
df8abd99 14=head1 SYNOPSIS
ee38fa40 15
df8abd99 16my $rs = MyApp::DB::Class->search(registered => 1);
17my @rows = MyApp::DB::Class->search(foo => 'bar');
ee38fa40 18
19=head1 DESCRIPTION
20
df8abd99 21The resultset is also known as an iterator. It is responsible for handling
22queries that may return an arbitrary number of rows, e.g. via C<search>
23or a C<has_many> relationship.
ee38fa40 24
25=head1 METHODS
26
df8abd99 27=head2 new($db_class, \%$attrs)
ee38fa40 28
df8abd99 29The resultset constructor. Takes a table class and an attribute hash
30(see below for more information on attributes). Does not perform
31any queries -- these are executed as needed by the other methods.
ee38fa40 32
33=cut
34
89c0a5a2 35sub new {
7624b19f 36 my ($it_class, $db_class, $attrs) = @_;
89c0a5a2 37 #use Data::Dumper; warn Dumper(@_);
38 $it_class = ref $it_class if ref $it_class;
39 $attrs = { %{ $attrs || {} } };
c7ce65e6 40 my %seen;
41 $attrs->{cols} ||= [ map { "me.$_" } $db_class->_select_columns ];
42 $attrs->{from} ||= [ { 'me' => $db_class->_table_name } ];
fef5d100 43 if ($attrs->{join}) {
c7ce65e6 44 foreach my $j (ref $attrs->{join} eq 'ARRAY'
45 ? (@{$attrs->{join}}) : ($attrs->{join})) {
46 if (ref $j eq 'HASH') {
47 $seen{$_} = 1 foreach keys %$j;
48 } else {
49 $seen{$j} = 1;
50 }
51 }
52 push(@{$attrs->{from}}, $db_class->_resolve_join($attrs->{join}, 'me'));
53 }
54 foreach my $pre (@{$attrs->{prefetch} || []}) {
55 push(@{$attrs->{from}}, $db_class->_resolve_join($pre, 'me'))
56 unless $seen{$pre};
57 push(@{$attrs->{cols}},
58 map { "$pre.$_" }
ddab752c 59 $db_class->_relationships->{$pre}->{class}->_select_columns);
fef5d100 60 }
89c0a5a2 61 my $new = {
62 class => $db_class,
fef5d100 63 cols => $attrs->{cols} || [ $db_class->_select_columns ],
89c0a5a2 64 cond => $attrs->{where},
fef5d100 65 from => $attrs->{from} || $db_class->_table_name,
3c5b25c5 66 count => undef,
67 pager => undef,
89c0a5a2 68 attrs => $attrs };
9229f20a 69 bless ($new, $it_class);
70 $new->pager if ($attrs->{page});
71 return $new;
89c0a5a2 72}
73
df8abd99 74=head2 cursor
ee38fa40 75
df8abd99 76Return a storage-driven cursor to the given resultset.
ee38fa40 77
78=cut
79
73f58123 80sub cursor {
81 my ($self) = @_;
82 my ($db_class, $attrs) = @{$self}{qw/class attrs/};
3c5b25c5 83 if ($attrs->{page}) {
84 $attrs->{rows} = $self->pager->entries_per_page;
85 $attrs->{offset} = $self->pager->skipped;
86 }
73f58123 87 return $self->{cursor}
fef5d100 88 ||= $db_class->storage->select($self->{from}, $self->{cols},
73f58123 89 $attrs->{where},$attrs);
90}
91
df8abd99 92=head2 slice($first, $last)
ee38fa40 93
df8abd99 94Returns a subset of elements from the resultset.
ee38fa40 95
96=cut
97
89c0a5a2 98sub slice {
99 my ($self, $min, $max) = @_;
100 my $attrs = { %{ $self->{attrs} || {} } };
101 $self->{class}->throw("Can't slice without where") unless $attrs->{where};
102 $attrs->{offset} = $min;
103 $attrs->{rows} = ($max ? ($max - $min + 1) : 1);
7624b19f 104 my $slice = $self->new($self->{class}, $attrs);
89c0a5a2 105 return (wantarray ? $slice->all : $slice);
106}
107
df8abd99 108=head2 next
ee38fa40 109
df8abd99 110Returns the next element in the resultset (undef is there is none).
ee38fa40 111
112=cut
113
89c0a5a2 114sub next {
115 my ($self) = @_;
73f58123 116 my @row = $self->cursor->next;
89c0a5a2 117 return unless (@row);
c7ce65e6 118 return $self->_construct_object(@row);
119}
120
121sub _construct_object {
122 my ($self, @row) = @_;
90f3f5ff 123 my @cols = @{ $self->{attrs}{cols} };
124 s/^me\.// for @cols;
3cbddeb1 125 @cols = grep { /\(/ or ! /\./ } @cols;
33ce49d6 126 my $new;
c7ce65e6 127 unless ($self->{attrs}{prefetch}) {
33ce49d6 128 $new = $self->{class}->_row_to_object(\@cols, \@row);
c7ce65e6 129 } else {
130 my @main = splice(@row, 0, scalar @cols);
33ce49d6 131 $new = $self->{class}->_row_to_object(\@cols, \@main);
c7ce65e6 132 PRE: foreach my $pre (@{$self->{attrs}{prefetch}}) {
133 my $rel_obj = $self->{class}->_relationships->{$pre};
dd417d06 134 my $pre_class = $self->{class}->resolve_class($rel_obj->{class});
135 my @pre_cols = $pre_class->_select_columns;
c7ce65e6 136 my @vals = splice(@row, 0, scalar @pre_cols);
dd417d06 137 my $fetched = $pre_class->_row_to_object(\@pre_cols, \@vals);
c7ce65e6 138 $self->{class}->throw("No accessor for prefetched $pre")
139 unless defined $rel_obj->{attrs}{accessor};
140 if ($rel_obj->{attrs}{accessor} eq 'single') {
141 foreach my $pri ($rel_obj->{class}->primary_columns) {
7cd300ea 142 unless (defined $fetched->get_column($pri)) {
143 undef $fetched;
144 last;
145 }
c7ce65e6 146 }
147 $new->{_relationship_data}{$pre} = $fetched;
148 } elsif ($rel_obj->{attrs}{accessor} eq 'filter') {
149 $new->{_inflated_column}{$pre} = $fetched;
150 } else {
3125eb1f 151 $self->{class}->throw("Don't know how to store prefetched $pre");
c7ce65e6 152 }
153 }
c7ce65e6 154 }
33ce49d6 155 $new = $self->{attrs}{record_filter}->($new)
156 if exists $self->{attrs}{record_filter};
157 return $new;
89c0a5a2 158}
159
df8abd99 160=head2 count
ee38fa40 161
df8abd99 162Performs an SQL C<COUNT> with the same query as the resultset was built
ee38fa40 163with to find the number of elements.
164
165=cut
166
89c0a5a2 167sub count {
168 my ($self) = @_;
7624b19f 169 my $db_class = $self->{class};
59f8e584 170 my $attrs = { %{ $self->{attrs} } };
3c5b25c5 171 unless ($self->{count}) {
172 # offset and order by are not needed to count
173 delete $attrs->{$_} for qw/offset order_by/;
174
175 my @cols = 'COUNT(*)';
fef5d100 176 $self->{count} = $db_class->storage->select_single($self->{from}, \@cols,
3c5b25c5 177 $self->{cond}, $attrs);
178 }
179 return 0 unless $self->{count};
9229f20a 180 return $self->{pager}->entries_on_this_page if ($self->{pager});
3c5b25c5 181 return ( $attrs->{rows} && $attrs->{rows} < $self->{count} )
59f8e584 182 ? $attrs->{rows}
3c5b25c5 183 : $self->{count};
89c0a5a2 184}
185
df8abd99 186=head2 all
ee38fa40 187
df8abd99 188Returns all elements in the resultset. Called implictly if the resultset
189is returned in list context.
ee38fa40 190
191=cut
192
89c0a5a2 193sub all {
194 my ($self) = @_;
c7ce65e6 195 return map { $self->_construct_object(@$_); }
73f58123 196 $self->cursor->all;
89c0a5a2 197}
198
df8abd99 199=head2 reset
ee38fa40 200
df8abd99 201Resets the resultset's cursor, so you can iterate through the elements again.
ee38fa40 202
203=cut
204
89c0a5a2 205sub reset {
206 my ($self) = @_;
73f58123 207 $self->cursor->reset;
89c0a5a2 208 return $self;
209}
210
df8abd99 211=head2 first
ee38fa40 212
df8abd99 213Resets the resultset and returns the first element.
ee38fa40 214
215=cut
216
89c0a5a2 217sub first {
218 return $_[0]->reset->next;
219}
220
df8abd99 221=head2 delete
ee38fa40 222
38659261 223Deletes all elements in the resultset.
ee38fa40 224
225=cut
226
28927b50 227sub delete {
89c0a5a2 228 my ($self) = @_;
229 $_->delete for $self->all;
230 return 1;
231}
232
28927b50 233*delete_all = \&delete; # Yeah, yeah, yeah ...
234
df8abd99 235=head2 pager
ee38fa40 236
237Returns a L<Data::Page> object for the current resultset. Only makes
238sense for queries with page turned on.
239
240=cut
241
3c5b25c5 242sub pager {
243 my ($self) = @_;
244 my $attrs = $self->{attrs};
245 delete $attrs->{offset};
246 my $rows_per_page = delete $attrs->{rows} || 10;
247 $self->{pager} ||= Data::Page->new(
248 $self->count, $rows_per_page, $attrs->{page} || 1);
249 $attrs->{rows} = $rows_per_page;
250 return $self->{pager};
251}
252
df8abd99 253=head2 page($page_num)
ee38fa40 254
df8abd99 255Returns a new resultset for the specified page.
ee38fa40 256
257=cut
258
3c5b25c5 259sub page {
260 my ($self, $page) = @_;
261 my $attrs = $self->{attrs};
262 $attrs->{page} = $page;
263 return $self->new($self->{class}, $attrs);
264}
265
076652e8 266=head1 Attributes
267
df8abd99 268The resultset takes various attributes that modify its behavior.
269Here's an overview of them:
076652e8 270
df8abd99 271=head2 order_by
076652e8 272
df8abd99 273Which column(s) to order the results by. This is currently passed
274through directly to SQL, so you can give e.g. C<foo DESC> for a
275descending order.
ee38fa40 276
df8abd99 277=head2 cols
ee38fa40 278
df8abd99 279Which columns should be retrieved.
ee38fa40 280
df8abd99 281=head2 join
ee38fa40 282
87980de7 283Contains a list of relationships that should be joined for this query. Can also
df8abd99 284contain a hash reference to refer to that relation's relations. So, if one column
285in your class C<belongs_to> foo and another C<belongs_to> bar, you can do
286C<< join => [qw/ foo bar /] >> to join both (and e.g. use them for C<order_by>).
287If a foo contains many margles and you want to join those too, you can do
288C<< join => { foo => 'margle' } >>. If you want to fetch the columns from the
289related table as well, see C<prefetch> below.
ee38fa40 290
87980de7 291=head2 prefetch
292
293Contains a list of relationships that should be fetched along with the main
294query (when they are accessed afterwards they will have already been
295"prefetched"). This is useful for when you know you will need the related
296object(s), because it saves a query. Currently limited to prefetching
297one relationship deep, so unlike C<join>, prefetch must be an arrayref.
298
df8abd99 299=head2 from
ee38fa40 300
df8abd99 301This attribute can contain a arrayref of elements. Each element can be another
ee38fa40 302arrayref, to nest joins, or it can be a hash which represents the two sides
303of the join.
304
df8abd99 305NOTE: Use this on your own risk. This allows you to shoot your foot off!
ee38fa40 306
df8abd99 307=head2 page
076652e8 308
df8abd99 309For a paged resultset, specifies which page to retrieve. Leave unset
310for an unpaged resultset.
076652e8 311
df8abd99 312=head2 rows
076652e8 313
df8abd99 314For a paged resultset, how many rows per page
076652e8 315
df8abd99 316=cut
076652e8 317
89c0a5a2 3181;