| File | /project/perl/lib/Data/Page.pm |
| Statements Executed | 15 |
| Statement Execution Time | 1.62ms |
| Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0s | 0s | Data::Page::BEGIN |
| 0 | 0 | 0 | 0s | 0s | Data::Page::current_page |
| 0 | 0 | 0 | 0s | 0s | Data::Page::entries_on_this_page |
| 0 | 0 | 0 | 0s | 0s | Data::Page::entries_per_page |
| 0 | 0 | 0 | 0s | 0s | Data::Page::first |
| 0 | 0 | 0 | 0s | 0s | Data::Page::first_page |
| 0 | 0 | 0 | 0s | 0s | Data::Page::last |
| 0 | 0 | 0 | 0s | 0s | Data::Page::last_page |
| 0 | 0 | 0 | 0s | 0s | Data::Page::new |
| 0 | 0 | 0 | 0s | 0s | Data::Page::next_page |
| 0 | 0 | 0 | 0s | 0s | Data::Page::previous_page |
| 0 | 0 | 0 | 0s | 0s | Data::Page::skipped |
| 0 | 0 | 0 | 0s | 0s | Data::Page::splice |
| 0 | 0 | 0 | 0s | 0s | Data::Page::total_entries |
| Line | State ments |
Time on line |
Calls | Time in subs |
Code |
|---|---|---|---|---|---|
| 1 | package Data::Page; | ||||
| 2 | 3 | 94µs | 1 | 240µs | use Carp; # spent 240µs making 1 call to Exporter::import |
| 3 | 3 | 81µs | 1 | 21µs | use strict; # spent 21µs making 1 call to strict::import |
| 4 | 3 | 121µs | 1 | 0s | use base 'Class::Accessor::Chained::Fast'; # spent 17.9ms making 1 call to base::import, recursion: max depth 2, time 17.9ms |
| 5 | 1 | 44µs | 1 | 691µs | __PACKAGE__->mk_accessors(qw(total_entries entries_per_page current_page)); # spent 691µs making 1 call to Class::Accessor::mk_accessors |
| 6 | |||||
| 7 | 3 | 1.26ms | 1 | 157µs | use vars qw($VERSION); # spent 157µs making 1 call to vars::import |
| 8 | 1 | 5µs | $VERSION = '2.00'; | ||
| 9 | |||||
| 10 | sub 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 | |||||
| 22 | sub 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 | |||||
| 32 | sub 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 | |||||
| 43 | sub total_entries { | ||||
| 44 | my $self = shift; | ||||
| 45 | if (@_) { | ||||
| 46 | return $self->_total_entries_accessor(@_); | ||||
| 47 | } | ||||
| 48 | return $self->_total_entries_accessor; | ||||
| 49 | } | ||||
| 50 | |||||
| 51 | sub 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 | |||||
| 61 | sub first_page { | ||||
| 62 | my $self = shift; | ||||
| 63 | |||||
| 64 | return 1; | ||||
| 65 | } | ||||
| 66 | |||||
| 67 | sub 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 | |||||
| 83 | sub 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 | |||||
| 93 | sub 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 | |||||
| 103 | sub 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 | |||||
| 113 | sub 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. | ||||
| 122 | sub 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 | |||||
| 129 | sub skipped { | ||||
| 130 | my $self = shift; | ||||
| 131 | |||||
| 132 | my $skipped = $self->first - 1; | ||||
| 133 | return 0 if $skipped < 0; | ||||
| 134 | return $skipped; | ||||
| 135 | } | ||||
| 136 | |||||
| 137 | 1 | 15µs | 1; | ||
| 138 | |||||
| 139 | __END__ | ||||
| 140 | |||||
| 141 | =head1 NAME | ||||
| 142 | |||||
| 143 | Data::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 | |||||
| 161 | When searching through large amounts of data, it is often the case | ||||
| 162 | that a result set is returned that is larger than we want to display | ||||
| 163 | on one page. This results in wanting to page through various pages of | ||||
| 164 | data. The maths behind this is unfortunately fiddly, hence this | ||||
| 165 | module. | ||||
| 166 | |||||
| 167 | The main concept is that you pass in the number of total entries, the | ||||
| 168 | number of entries per page, and the current page number. You can then | ||||
| 169 | call methods to find out how many pages of information there are, and | ||||
| 170 | what number the first and last entries on the current page really are. | ||||
| 171 | |||||
| 172 | For example, say we wished to page through the integers from 1 to 100 | ||||
| 173 | with 20 entries per page. The first page would consist of 1-20, the | ||||
| 174 | second page from 21-40, the third page from 41-60, the fourth page | ||||
| 175 | from 61-80 and the fifth page from 81-100. This module would help you | ||||
| 176 | work this out. | ||||
| 177 | |||||
| 178 | =head1 METHODS | ||||
| 179 | |||||
| 180 | =head2 new | ||||
| 181 | |||||
| 182 | This is the constructor, which takes no arguments. | ||||
| 183 | |||||
| 184 | my $page = Data::Page->new(); | ||||
| 185 | |||||
| 186 | There is also an old, deprecated constructor, which currently takes | ||||
| 187 | two mandatory arguments, the total number of entries and the number of | ||||
| 188 | entries 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 | |||||
| 194 | This method get or sets the total number of entries: | ||||
| 195 | |||||
| 196 | print "Entries:", $page->total_entries, "\n"; | ||||
| 197 | |||||
| 198 | =head2 entries_per_page | ||||
| 199 | |||||
| 200 | This method gets or sets the total number of entries per page (which | ||||
| 201 | defaults to 10): | ||||
| 202 | |||||
| 203 | print "Per page:", $page->entries_per_page, "\n"; | ||||
| 204 | |||||
| 205 | =head2 current_page | ||||
| 206 | |||||
| 207 | This 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 | |||||
| 213 | This 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 | |||||
| 219 | This method returns the first page. This is put in for reasons of | ||||
| 220 | symmetry with last_page, as it always returns 1: | ||||
| 221 | |||||
| 222 | print "Pages range from: ", $page->first_page, "\n"; | ||||
| 223 | |||||
| 224 | =head2 last_page | ||||
| 225 | |||||
| 226 | This method returns the total number of pages of information: | ||||
| 227 | |||||
| 228 | print "Pages range to: ", $page->last_page, "\n"; | ||||
| 229 | |||||
| 230 | =head2 first | ||||
| 231 | |||||
| 232 | This 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 | |||||
| 238 | This 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 | |||||
| 244 | This method returns the previous page number, if one exists. Otherwise | ||||
| 245 | it returns undefined: | ||||
| 246 | |||||
| 247 | if ($page->previous_page) { | ||||
| 248 | print "Previous page number: ", $page->previous_page, "\n"; | ||||
| 249 | } | ||||
| 250 | |||||
| 251 | =head2 next_page | ||||
| 252 | |||||
| 253 | This method returns the next page number, if one exists. Otherwise | ||||
| 254 | it returns undefined: | ||||
| 255 | |||||
| 256 | if ($page->next_page) { | ||||
| 257 | print "Next page number: ", $page->next_page, "\n"; | ||||
| 258 | } | ||||
| 259 | |||||
| 260 | =head2 splice | ||||
| 261 | |||||
| 262 | This method takes in a listref, and returns only the values which are | ||||
| 263 | on the current page: | ||||
| 264 | |||||
| 265 | @visible_holidays = $page->splice(\@holidays); | ||||
| 266 | |||||
| 267 | =head2 skipped | ||||
| 268 | |||||
| 269 | This method is useful paging through data in a database using SQL | ||||
| 270 | LIMIT 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 | |||||
| 279 | It has been said before that this code is "too simple" for CPAN, but I | ||||
| 280 | must disagree. I have seen people write this kind of code over and | ||||
| 281 | over again and they always get it wrong. Perhaps now they will spend | ||||
| 282 | more time getting the rest of their code right... | ||||
| 283 | |||||
| 284 | =head1 SEE ALSO | ||||
| 285 | |||||
| 286 | Related modules which may be of interest: L<Data::Pageset>, | ||||
| 287 | L<Data::Page::Tied>, L<Data::SpreadPagination>. | ||||
| 288 | |||||
| 289 | =head1 AUTHOR | ||||
| 290 | |||||
| 291 | Based on code originally by Leo Lapworth, with many changes added by | ||||
| 292 | by Leon Brocard <acme@astray.com>. | ||||
| 293 | |||||
| 294 | =head1 COPYRIGHT | ||||
| 295 | |||||
| 296 | Copyright (C) 2000-4, Leon Brocard | ||||
| 297 | |||||
| 298 | This module is free software; you can redistribute it or modify it | ||||
| 299 | under the same terms as Perl itself. | ||||
| 300 | |||||
| 301 |