=pod ##################################################################################### =head1 NAME Apache::Voodoo::Pager - Provides generic pagination controls =head1 VERSION $Id: Pager.pm 6499 2008-01-17 15:55:40Z medwards $ =head1 SYNOPSIS This module generates all the necessary 'next', 'previous' and page number links typically found on any search engine results page. This module can be used in any scenario where data must be paginated. my $pager = Apache::Voodoo::Pager->new('count' => 40, 'window' => 10, 'limit' => 500, 'persist' => [ 'url_param', ... ]); $pager->set_configuration('count' => 40, 'window' => 10, 'limit' => 500, 'persist' => [ 'url_param', ... ]); my $template_params = $pager->paginate($all_url_params,$number_of_rows_in_results); =cut ################################################################################ package Apache::Voodoo::Pager; use strict; use POSIX qw(ceil); use Data::Dumper; sub new { my $class = shift; my $self = {}; bless $self, $class; $self->set_configuration(@_); return $self; } sub set_configuration { my $self = shift; my %c = @_; $c{'count'} =~ s/\D//g; $self->{'count'} = $c{'count'} || 40; $c{'window'} =~ s/\D//g; $self->{'window'} = $c{'window'} || 10; $c{'limit'} =~ s/\D//g; $self->{'limit'} = $c{'limit'} || 500; $self->{'persist'} = $c{'persist'}; } sub paginate { my $self = shift; my $params = shift; my $res_count = shift; $params->{'count'} =~ s/\D//g; $params->{'page'} =~ s/\D//g; $params->{'showall'} =~ s/\D//g; my $count = $params->{'count'} || $self->{'count'}; my $page = $params->{'page'} || 1; my $showall = $params->{'showall'} || 0; my %output; if ($res_count > $count) { my $url_params = "count=$count&" . join('&', map { $_ .'='. $params->{$_} } @{$self->{'persist'}}); $output{'MODE_PARAMS'} = $url_params; $output{'HAS_MORE'} = 1; if ($res_count < $self->{'limit'} && $showall) { $output{'SHOW_MODE'} = 1; $output{'SHOW_ALL'} = 1; $output{'MODE_PARAMS'} .= "&showall=0"; } else { if ($res_count < $self->{'limit'}) { $output{'MODE_PARAMS'} .= "&showall=1"; $output{'SHOW_MODE'} = 1; } # setup the page list my $numpages = ceil($res_count / $count); $output{'PAGE_NUMBER'} = $page; $output{'NUMBER_PAGES'} = $numpages; if ($numpages > 1) { # setup sliding window of page numbers my $start = 0; my $window = $self->{'window'}; my $end = $window; if ($page >= $window) { $end = $page + ceil($window / 2) - 1; $start = $end - $window; } if ($end > $numpages) { $end = $numpages; } $output{'PAGES'} = []; for (my $x = $start; $x < $end; $x++) { # Put the page info into the array push(@{$output{'PAGES'}}, { 'NOT_ME' => (($x + 1) == $page)?0:1, 'PAGE' => ($x + 1), 'NOT_LAST' => 1, 'URL_PARAMS' => $url_params . '&page='. ($x + 1) } ); } # prevent access of index -1 if the page number requested is beyond the range. if ($#{$output{'PAGES'}} >= 0) { # set the last page to last $output{'PAGES'}->[$#{$output{'PAGES'}}]->{'NOT_LAST'} = 0; } # setup the 'more link' if ($end != $numpages) { $output{'MORE_URL_PARAMS'} = $url_params . '&page=' . ($end + 1); $output{'MORE_PAGE'} = $end+1; } # setup the preivous link if ($page > 1) { $output{'HAS_PREVIOUS'} = 1; $output{'PREVIOUS_URL_PARAMS'} = $url_params . '&page=' . ($page - 1); $output{'PREV_PAGE'} = $page-1; } # setup the next link if ($page * $count < $res_count) { $output{'HAS_NEXT'} = 1; $output{'NEXT_URL_PARAMS'} = $url_params . '&page=' . ($page + 1); $output{'NEXT_PAGE'} = $page+1; } } } } return %output; } 1; =pod ################################################################################ =head1 PARAMETERS =over 4 =item count Number of items per page =item window Number of page numbers to appear at once. window => 10 would yield links for page numbers 1 -> 10 =item limit Maximum number of rows in the result that can be displayed at once. In other words if limit is set to 100 and the result set contains 101 items, then the 'Show all' link will be disabled. =item persist List of url parameters that should appear in every link generated by this module. search parameters, sort options, etc, etc. =back =head1 METHODS =over 4 =item paginate() returns a hashref suitable for passing to L using the example template below. The entire set of url paramaters is required so that Apache::Voodoo::Pager can get access to it's own parameters as well as those listed in the persist => [] configuration parameter. Apache::Voodoo::Pager uses two internal paramaters, 'page' and 'showall' to keep track of internal state. page is the page number of the currently displayed result set (1 origin indexed) and showall is set to 1 when the entire result set is being displayed at once. These values can be used by the caller to determine how to properly cut the result set. =back =head1 VOODOO EXAMPLE use Apache::Voodoo::Pager; sub init { my $self = shift; $self->{'pager'} = Apache::Voodoo::Pager->new( 'count' => 10, 'window' => 5, 'limit' => 100, 'perisist' => [ 'value' ] ); } sub list { my $self = shift; my $p = shift; my $params = $p->{'params'}; my $dbh = $p->{'dbh'}; my $value = $params->{'value'}; my $count = $params->{'count'} || 10; my $page = $params->{'page'} || 1; my $showall = $params->{'showall'} || 0; # Parameter validation and other security checks omitted for brevity my $stmt = "SELECT SQL_CALC_FOUND_ROWS col1,col2,col3 FROM some_table WHERE col1 = ?"; unless ($showall) { $stmt .= " LIMIT $count OFFSET ". $count * ($page-1); } my $results = $dbh->selectall_arrayref($stmt,undef,$value); my $matches = $dbh->selectall_arrayref("SELECT FOUND_ROWS()"); my $template_stuff = $self->{'pager'}->paginate($params,$matches->[0]->[0]); # other stuff this method does } =head1 EXAMPLE HTML::Template | | More... Previous | Next | Show Page Show All Page of =head1 AUTHOR Maverick, /\/\averick@smurfbaneDOTorg =head1 COPYRIGHT Copyright (c) 2005 Steven Edwards. All rights reserved. You may use and distribute Voodoo under the terms described in the LICENSE file include in this package or L. The summary is it's a legalese version of the Artistic License :) =cut ################################################################################