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 | BEGIN | Data::Page::
0 | 0 | 0 | 0s | 0s | current_page | Data::Page::
0 | 0 | 0 | 0s | 0s | entries_on_this_page | Data::Page::
0 | 0 | 0 | 0s | 0s | entries_per_page | Data::Page::
0 | 0 | 0 | 0s | 0s | first | Data::Page::
0 | 0 | 0 | 0s | 0s | first_page | Data::Page::
0 | 0 | 0 | 0s | 0s | last | Data::Page::
0 | 0 | 0 | 0s | 0s | last_page | Data::Page::
0 | 0 | 0 | 0s | 0s | new | Data::Page::
0 | 0 | 0 | 0s | 0s | next_page | Data::Page::
0 | 0 | 0 | 0s | 0s | previous_page | Data::Page::
0 | 0 | 0 | 0s | 0s | skipped | Data::Page::
0 | 0 | 0 | 0s | 0s | splice | Data::Page::
0 | 0 | 0 | 0s | 0s | total_entries | Data::Page::
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 |