1package Data::Page; 2use Carp; 3use strict; 4use base 'Class::Accessor::Chained::Fast'; 5__PACKAGE__->mk_accessors(qw(total_entries entries_per_page current_page)); 6 7use vars qw($VERSION); 8$VERSION = '2.00'; 9 10sub new { 11 my $class = shift; 12 my $self = {}; 13 bless($self, $class); 14 15 my ($total_entries, $entries_per_page, $current_page) = @_; 16 $self->total_entries($total_entries || 0); 17 $self->entries_per_page($entries_per_page || 10); 18 $self->current_page($current_page || 1); 19 return $self; 20} 21 22sub entries_per_page { 23 my $self = shift; 24 my $entries_per_page = $_[0]; 25 if (@_) { 26 croak("Fewer than one entry per page!") if $entries_per_page < 1; 27 return $self->_entries_per_page_accessor(@_); 28 } 29 return $self->_entries_per_page_accessor(); 30} 31 32sub current_page { 33 my $self = shift; 34 if (@_) { 35 return $self->_current_page_accessor(@_); 36 } 37 return $self->first_page unless defined $self->_current_page_accessor; 38 return $self->first_page if $self->_current_page_accessor < $self->first_page; 39 return $self->last_page if $self->_current_page_accessor > $self->last_page; 40 return $self->_current_page_accessor(); 41} 42 43sub total_entries { 44 my $self = shift; 45 if (@_) { 46 return $self->_total_entries_accessor(@_); 47 } 48 return $self->_total_entries_accessor; 49} 50 51sub entries_on_this_page { 52 my $self = shift; 53 54 if ($self->total_entries == 0) { 55 return 0; 56 } else { 57 return $self->last - $self->first + 1; 58 } 59} 60 61sub first_page { 62 my $self = shift; 63 64 return 1; 65} 66 67sub last_page { 68 my $self = shift; 69 70 my $pages = $self->total_entries / $self->entries_per_page; 71 my $last_page; 72 73 if ($pages == int $pages) { 74 $last_page = $pages; 75 } else { 76 $last_page = 1 + int($pages); 77 } 78 79 $last_page = 1 if $last_page < 1; 80 return $last_page; 81} 82 83sub first { 84 my $self = shift; 85 86 if ($self->total_entries == 0) { 87 return 0; 88 } else { 89 return (($self->current_page - 1) * $self->entries_per_page) + 1; 90 } 91} 92 93sub last { 94 my $self = shift; 95 96 if ($self->current_page == $self->last_page) { 97 return $self->total_entries; 98 } else { 99 return ($self->current_page * $self->entries_per_page); 100 } 101} 102 103sub previous_page { 104 my $self = shift; 105 106 if ($self->current_page > 1) { 107 return $self->current_page - 1; 108 } else { 109 return undef; 110 } 111} 112 113sub next_page { 114 my $self = shift; 115 116 $self->current_page < $self->last_page ? $self->current_page + 1 : undef; 117} 118 119# This method would probably be better named 'select' or 'slice' or 120# something, because it doesn't modify the array the way 121# CORE::splice() does. 122sub splice { 123 my ($self, $array) = @_; 124 my $top = @$array > $self->last ? $self->last : @$array; 125 return () if $top == 0; # empty 126 return @{$array}[ $self->first - 1 .. $top - 1 ]; 127} 128 129sub skipped { 130 my $self = shift; 131 132 my $skipped = $self->first - 1; 133 return 0 if $skipped < 0; 134 return $skipped; 135} 136 1371; 138 139__END__ 140 141=head1 NAME 142 143Data::Page - help when paging through sets of results 144 145=head1 SYNOPSIS 146 147 use Data::Page; 148 149 my $page = Data::Page->new(); 150 $page->total_entries($total_entries); 151 $page->entries_per_page($entries_per_page); 152 $page->current_page($current_page); 153 154 print " First page: ", $page->first_page, "\n"; 155 print " Last page: ", $page->last_page, "\n"; 156 print "First entry on page: ", $page->first, "\n"; 157 print " Last entry on page: ", $page->last, "\n"; 158 159=head1 DESCRIPTION 160 161When searching through large amounts of data, it is often the case 162that a result set is returned that is larger than we want to display 163on one page. This results in wanting to page through various pages of 164data. The maths behind this is unfortunately fiddly, hence this 165module. 166 167The main concept is that you pass in the number of total entries, the 168number of entries per page, and the current page number. You can then 169call methods to find out how many pages of information there are, and 170what number the first and last entries on the current page really are. 171 172For example, say we wished to page through the integers from 1 to 100 173with 20 entries per page. The first page would consist of 1-20, the 174second page from 21-40, the third page from 41-60, the fourth page 175from 61-80 and the fifth page from 81-100. This module would help you 176work this out. 177 178=head1 METHODS 179 180=head2 new 181 182This is the constructor, which takes no arguments. 183 184 my $page = Data::Page->new(); 185 186There is also an old, deprecated constructor, which currently takes 187two mandatory arguments, the total number of entries and the number of 188entries per page. It also optionally takes the current page number: 189 190 my $page = Data::Page->new($total_entries, $entries_per_page, $current_page); 191 192=head2 total_entries 193 194This method get or sets the total number of entries: 195 196 print "Entries:", $page->total_entries, "\n"; 197 198=head2 entries_per_page 199 200This method gets or sets the total number of entries per page (which 201defaults to 10): 202 203 print "Per page:", $page->entries_per_page, "\n"; 204 205=head2 current_page 206 207This method gets or sets the current page number (which defaults to 1): 208 209 print "Page: ", $page->current_page, "\n"; 210 211=head2 entries_on_this_page 212 213This methods returns the number of entries on the current page: 214 215 print "There are ", $page->entries_on_this_page, " entries displayed\n"; 216 217=head2 first_page 218 219This method returns the first page. This is put in for reasons of 220symmetry with last_page, as it always returns 1: 221 222 print "Pages range from: ", $page->first_page, "\n"; 223 224=head2 last_page 225 226This method returns the total number of pages of information: 227 228 print "Pages range to: ", $page->last_page, "\n"; 229 230=head2 first 231 232This method returns the number of the first entry on the current page: 233 234 print "Showing entries from: ", $page->first, "\n"; 235 236=head2 last 237 238This method returns the number of the last entry on the current page: 239 240 print "Showing entries to: ", $page->last, "\n"; 241 242=head2 previous_page 243 244This method returns the previous page number, if one exists. Otherwise 245it returns undefined: 246 247 if ($page->previous_page) { 248 print "Previous page number: ", $page->previous_page, "\n"; 249 } 250 251=head2 next_page 252 253This method returns the next page number, if one exists. Otherwise 254it returns undefined: 255 256 if ($page->next_page) { 257 print "Next page number: ", $page->next_page, "\n"; 258 } 259 260=head2 splice 261 262This method takes in a listref, and returns only the values which are 263on the current page: 264 265 @visible_holidays = $page->splice(\@holidays); 266 267=head2 skipped 268 269This method is useful paging through data in a database using SQL 270LIMIT clauses. It is simply $page->first - 1: 271 272 $sth = $dbh->prepare( 273 q{SELECT * FROM table ORDER BY rec_date LIMIT ?, ?} 274 ); 275 $sth->execute($date, $page->skipped, $page->entries_per_page); 276 277=head1 NOTES 278 279It has been said before that this code is "too simple" for CPAN, but I 280must disagree. I have seen people write this kind of code over and 281over again and they always get it wrong. Perhaps now they will spend 282more time getting the rest of their code right... 283 284=head1 SEE ALSO 285 286Related modules which may be of interest: L<Data::Pageset>, 287L<Data::Page::Tied>, L<Data::SpreadPagination>. 288 289=head1 AUTHOR 290 291Based on code originally by Leo Lapworth, with many changes added by 292by Leon Brocard <acme@astray.com>. 293 294=head1 COPYRIGHT 295 296Copyright (C) 2000-4, Leon Brocard 297 298This module is free software; you can redistribute it or modify it 299under the same terms as Perl itself. 300 301 302