1package Class::DBI::Iterator; 2 3=head1 NAME 4 5Class::DBI::Iterator - Iterate over Class::DBI search results 6 7=head1 SYNOPSIS 8 9 my $it = My::Class->search(foo => 'bar'); 10 11 my $results = $it->count; 12 13 my $first_result = $it->first; 14 while ($it->next) { ... } 15 16 my @slice = $it->slice(10,19); 17 my $slice = $it->slice(10,19); 18 19 $it->reset; 20 21 $it->delete_all; 22 23=head1 DESCRIPTION 24 25Any Class::DBI search (including a has_many method) which returns multiple 26objects can be made to return an iterator instead simply by executing 27the search in scalar context. 28 29Then, rather than having to fetch all the results at the same time, you 30can fetch them one at a time, potentially saving a considerable amount 31of processing time and memory. 32 33=head1 CAVEAT 34 35Note that there is no provision for the data changing (or even being 36deleted) in the database inbetween performing the search and retrieving 37the next result. 38 39=cut 40 41use strict; 42use overload 43 '0+' => 'count', 44 fallback => 1; 45 46sub new { 47 my ($me, $them, $data, @mapper) = @_; 48 bless { 49 _class => $them, 50 _data => $data, 51 _mapper => [@mapper], 52 _place => 0, 53 }, 54 ref $me || $me; 55} 56 57sub set_mapping_method { 58 my ($self, @mapper) = @_; 59 $self->{_mapper} = [@mapper]; 60 $self; 61} 62 63sub class { shift->{_class} } 64sub data { @{ shift->{_data} } } 65sub mapper { @{ shift->{_mapper} } } 66 67sub count { 68 my $self = shift; 69 $self->{_count} ||= scalar $self->data; 70} 71 72sub next { 73 my $self = shift; 74 my $use = $self->{_data}->[ $self->{_place}++ ] or return; 75 my @obj = ($self->class->construct($use)); 76 foreach my $meth ($self->mapper) { 77 @obj = map $_->$meth(), @obj; 78 } 79 warn "Discarding extra inflated objects" if @obj > 1; 80 return $obj[0]; 81} 82 83sub first { 84 my $self = shift; 85 $self->reset; 86 return $self->next; 87} 88 89sub slice { 90 my ($self, $start, $end) = @_; 91 $end ||= $start; 92 $self->{_place} = $start; 93 my @return; 94 while ($self->{_place} <= $end) { 95 push @return, $self->next || last; 96 } 97 return @return if wantarray; 98 99 my $slice = $self->new($self->class, \@return, $self->mapper,); 100 return $slice; 101} 102 103sub delete_all { 104 my $self = shift; 105 my $count = $self->count or return; 106 $self->first->delete; # to reset counter 107 while (my $obj = $self->next) { 108 $obj->delete; 109 } 110 $self->{_data} = []; 111 1; 112} 113 114sub reset { shift->{_place} = 0 } 115 1161; 117