Data::Paginate - Perl extension for complete and efficient data pagination


   use Data::Paginate;
   my $pgr = Data::Paginate->new(\%settings);


This module gives you a single resource to paginate data very simply.

It includes access to the page/data variables as well as a way to generate the navigation HTML and get the data for the current page from a list of data and many many other things. It can definately be extended to generate the navigation cotrols for XML, Tk, Flash, Curses, etc... (See "SUBCLASSING" below)

Each item in the "new()" and "Non new() entries" sections have a get_ and set_ method unless otherwise specified.

By that I mean if the "item" is "foo" then you can set it with $pgr->set_foo($new_value) and get it with $pgr->get_foo()...


Its only argument can be a hashref where its keys are the names documented below in sections that say it can be specified in new().

Also, total_entries is the item that makes the most sense to use if you're only using one :)

Attributes that recalculate the page dynamics

These all have get_ and set_ methods and can be specified in the hashref to new()

total_entries (100)

This is the number of pieces of data to paginate.

When set it can be a digit or an array reference (whose number of elements is used as the digit)

entries_per_page (10)

The number of the "total_entries" that go in each page.

pages_per_set (10)

If set to a digit greater than 0 it turns on the use of "sets" in the object and tells it how many pages are to be in each set.

This is very handy to make the navigation easier to use. Say you have data that is paginated to 1000 pages.

If you set this to, say 20, you'd see navigation for pages 1..20, then 21..30, etc etc instead of 1..1000 which would be ungainly.

The use of sets is encouraged but you can turn it off by setting it to 0.

sets_per_set (pages_per_set)

If sets are in use then this is how many sets to show in the navigation. So if there are 100 sets and its set to 10 it will show 11-20 if you are on say set 12.

current_page (1)

The current page of the data set.

No set_ method. (IE it needs to be specified in new() or via the param handler (which is also set in new())

variable_entries_per_page ({})

An optional hashref whose key is the page number and the value is the number of entries for that page.

For example to make all your data paginated as a haiku:

       '1' => '5',
       '2' => '7',
       '3' => '5',

Page 1 will have 5 records, page 2 will have 7 records, and page 3 will have 5 records.

Pages 4 on will have "entries_per_page" records.

It is ok to not specify them in any sort of range or run:

       '34' => '42',
       '55' => '78',
       '89' => '99',

Some display settings that require specific types of values.

These all have get_ and set_ methods and can be specified in the hashref to new().

Their argument in assignments must be the same type as the default values.

page_result_display_map ({})

An optional hashref whose key is the page number and the value is what to use in the navigation instead of the digit.

set_result_display_map ({})

An optional hashref whose key is the set number and the value is what to use in the navigation instead of the digit.

result_display_map ({})

An optional hashref that sets page_result_display_map and set_result_display_map to the same thing.

There is no get_ method for this.

html_line_white_space (0)

A digit that specifies the number of spaces to indent the HMTL in any get_*_html functions.


A CODE reference to handle the parameres. See source if you have a need to modify this.

There is no get_ method for this.

sets_in_rows (0)

Not currently used, see TODO.

Misc (HTML) display settings

All have get_ and set_ methods and can be specfied in new()

pre_current_page (»)
pst_current_page («)
pst_current_set (»)
pre_current_set («)
total_entries_param (te)
set_param (st)
next_page_html (Next Page →)
page_param (pg)
simple_nav (0)
cssid_set (set)
cssid_not_current_page (notpg)
cssid_current_set (curst)
pre_not_current_set ([)
pre_not_current_page ([)
pst_not_current_set (])
pst_not_current_page (])
prev_set_html (← Prev Set)
one_set_hide (0)
no_prev_set_html ('')
as_table (0)
style (style tag that centers "#page" and "#set"
no_prev_page_html ('')
one_page_hide (0)
next_set_html (Next Set →)
one_set_html ('')
no_next_page_html ('')
cssid_current_page (curpg)
no_next_set_html ('')
prev_page_html (← Prev Page)
cssid_page (page)
cssid_not_current_set (notst)
use_of_vars (0)
one_page_html ('')
of_page_string (Page)
of_set_string (Set)
of_of_string (of)
of_page_html ('')
of_set_html ('')

Non new() entries

Data that gets set during calculation.

Each has a get_ function but does not have a set_ funtion and cannot be specified in new()


The number of entries on the page, its always "entries_per_page" except when you are on the last page and there are less than "entries_per_page" left.


The first page number, its always 1.


The last page number.


first record in page counting from 1 not 0


last record on page counting from 1 not 0


Number of the previous page. 0 if currently on page 1


Number of the next page. 0 if currently on last page.


Number of the current set


Number of the previous set. 0 if currently on set 1


Number of the next set. 0 if currently on last set.


The number of pages in this set, its always "pages_per_set" except when you are on the last set and there are less than "pages_per_set" left.


Number of last set.


Number of first set, always 1


Page number of the last page in the set.


Page number of the first page in the set.


First set in this display's "sets_per_set" range.


Last set in this display's "sets_per_set" range.

Other methods


Get HTML navigation for the object's current state.

In scalar context returns a single string with the HTML navigation.

In array context returns the page HTML as the first item and the set HTML as the second.

If simple_nav is true it returns a single string regardless of context.

    print scalar $pgr->get_navi_html();


See "to do"

get_ misc data (IE no set_)


Returns an array of numbers that are indexes of the current page's range on the data array.


Returns an array of the current page's data as sliced for the given arrayref.

    my @data_to_display = $pgr->get_pages_slice(\@orig_data);

Same as get_pages_splice but returns an array ref instead of an array.


In array context returns $pgr->get_first() and $pgr->get_last() as its items. In scalar context it returns a stringified, comma seperated version.

    my($first, $last)  = $pgr->get_firstlast(); # '1' and '10' respectively
    my $first_last_csv = $pgr->get_firstlast(); # '1,10'

In array context returns $pgr->get_last() and $pgr->get_first() as its items. In scalar context it returns a stringified, comma seperated version.

    my($last, $first)  = $pgr->get_lastfirst(); # '10' and '1' respectively
    my $last_first_csv = $pgr->get_lastfirst(); # '10,1'

Returns a hashref that is a snapshot of the current state of the object. Useful for debugging and development.


Example using module to not only paginate easily but optimize database calls:

    # set total_entries *once* then pass it around 
    # in the object's links from then on for efficiency:
    my ($total_entries) = CGI::param('te') =~ m/^\d+$/ && CGI::param('te') > 0
        ? CGI::param('te') 
        : $dbh->select_rowarray("SELECT COUNT(*) FROM baz WHERE $where");

    my $pgr = Data::Paginate->new({ total_entries => $total_entries });

    # only SELECT current page's records:
    # Hint: set 'data_html_config's 'start_array_index_at_zero' to true if you are using 'data_html_config'
    #   that way the array index pass to idx_handlers for the array of records for this page (IE the LIMITed records) 
    # LIMIT $pgr->get_entries_per_page() OFFSET ($pgr->get_first() - 1)
    my $query = "SELECT foo, bar FROM baz WHERE $where LIMIT ? OFFSET ?";

    print scalar $pgr->get_navi_html();

    for my $record (@{ $dbh->selectall_arrayref($query, undef, $pgr->get_entries_per_page(), ($pgr->get_first() - 1)) }) {
        # display $record here

    print scalar $pgr->get_navi_html();

Example to keep the 'te' parameter safe from being spoofed (and do same optimization as above) - HIGHLY RECOMMENDED:

    my $verify        = CGI::param('ve') || '';
    my $total_entries = int( CGI::param('te') );
    my $te_match      = $total_entries ? Digest::MD5::md5_hex("unique_cypher-$total_entries-$where") : '';
    if(!$total_entries || $verify ne $te_match) {
        # its not ok so re-fetch
        ($total_entries) = $dbh->select_rowarray("SELECT COUNT(*) FROM baz WHERE $where");
        $te_match        = Digest::MD5::md5_hex("unique_cypher-$total_entries-$where");
    # otherwise its all ok so use it 
    my $pgr = Data::Paginate->new({ 
        'total_entries' => $total_entries,
        'total_entries_verify_param_value' => $te_match,


If you'd like to add functionality to this module *please* do it correctly. Part of the reason I made this module was that similar modules had functionality spread out among several modules that did not use the namespace model or subclassing paradigm correctly and made it really confusing and difficult to use.

So say you want to add functionality for TMBG please do it like so:

- use "Data::Paginate::TMBG" as the package name.

- use Data::Paginate; in your module (use base 'Data::Paginate'; for inheritence)

- make the method name like so (or prefix w/ Data::Paginate:: if !use base, but why would you do that ;p):

    sub get_navi_tmbg { # each subclass should have a get_navi_* function so its use is consistent
         my ($pgr) = @_; # Data::Paginate Object

    sub make_a_little_birdhouse_in_your_soul {
         my ($pgr) = @_; # Data::Paginate Object

That way it can be used like so:

    use Data::Paginate::TMBG; # no need to use Data::Paginate in the script since your module will use() it for use in its method(s)

    my $pgr = Data::Paginate->new({ total_entries => $total_entries }):

    $pgr->make_a_little_birdhouse_in_your_soul({ 'say' => q{I'm the only bee in your bonnet} }); # misc function to do whatever you might need

    print $pgr->get_navi_tmbg();


- POD and Changelog for 0.0.2 and 0.0.3

  data_na CSS class
  total_entries_verify_param_name (ve)
  total_entries_verify_param_value ('')

  [get|set]_data_html_config + new()
  [get|set]_perpage_html_config + new()
  get_data_html() (plus style{} addition)
  get_perpage_html_select() IE: get_perpage_html(1)

- Support Locale::Maketext handles for output in any language

- A few additions to get_navi_html()

- Improve POD documentation depending on feedback.


Daniel Muey,


Copyright 2005 by Daniel Muey

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.