ClearPress Framework

Paging Lists

Commonly in listings you'll want to add paging. This can be quite a nuisance (in particular if the results are lists of searches or have customisable parameters which slice and dice the result set in different ways). ClearPress takes some (but not all) of that pain away.

Model

Firstly the data model needs to have support for a paged list. In this case we'll take a start for the page, a length of the page and an order-by column, all of which will be passed through to the database layer as part of the query.

sub bounded_users { my ($self, $start, $len, $orderby) = @_; my $pkg = ref $self; my $order = $orderby ? qq[ORDER BY $orderby]: q[]; my $query = qq[SELECT @{[join q[, ], $pkg->fields]} FROM @{[$pkg->table]} $order]; my $util = $self->util; my $driver = $util->driver; my $bounded = $driver->bounded_select($query, $len, $start); return $self->gen_getarray($pkg, $bounded); }

Note that the limit with start and len isn't built in SQL - this is left to the driver layer, because different database engines do this in different ways.

View

The view is given support for handling the additional CGI parameters for paging. These are checked with rudimentary validation and passed through to the model's new paging call, and also stuffed in to the model (bad encapsulation!) for the template to pick up in a moment.

use Readonly; Readonly::Scalar our $MAX_LEN => 50; Readonly::Scalar our $MIN_LEN => 10; sub list { my $self = shift; my $util = $self->util; my $cgi = $util->cgi; my $start = $cgi->param('start'); my $len = $cgi->param('len'); my $orderby = $cgi->param('orderby'); my $model = $self->model; my $safe_orderby = $model->secondary_key; if($orderby) { for my $f ($model->fields) { if($f eq $orderby) { $safe_orderby = $f; } } } if(!$len || $len < $MIN_LEN) { $len = $MIN_LEN; } if(!$start || $start < 0) { $start = 0; } if($len > $MAX_LEN) { $len = $MAX_LEN; } $model->{users} = $model->bounded_users($start, $len, $safe_orderby); $model->{start} = $start; $model->{len} = $len; $model->{orderby} = $orderby; return 1; }

Template

Displaying [% model.len %] of [% model.count_users %] users. [%- USE Math %] [%- SET pager_limit = model.len ; SET pager_start = model.start ; SET pager_max = model.count_users ; SET pager_orderby = model.orderby %] [%- SET p_last = pager_limit * Math.int(pager_max / pager_limit); %] [%- SET p_last = p_last - pager_limit IF p_last == pager_max %] [%- SET p_prev = pager_start - pager_limit %] [%- SET p_prev = 0 IF p_prev < 0 %] [%- SET p_next = pager_start + pager_limit %] [%- SET p_next = p_last IF p_next > p_last %] [%- SET pager_extra = "orderby=$pager_orderby" %] <div class="pager"> <form id="pager_limit_selection" method="get" action="#"> <input type="hidden" name="start" value="[% pager_start | url %]"/> <input type="hidden" name="orderby" value="[% model.orderby | url %]"/> <label for="len">Display <select id="len" name="len"> [%- FOREACH i = [10, 20, 30, 40, 50] %] <option value="[%i%]"[% IF i==pager_limit %] selected="selected"[% END %]> [%i%] </option> [%- END %] </select> entries per page</label> <a href="?[% pager_extra %];start=0;len=[% pager_limit %]">⇐</a> <a href="?[% pager_extra %];start=[% p_prev %];len=[% pager_limit %]">←</a> [% 1 + pager_start / pager_limit %] / [% 1 + p_last / pager_limit %] <a href="?[% pager_extra %];start=[% p_next %];len=[% pager_limit %]">→</a> <a href="?[% pager_extra %];start=[% p_last %];len=[% pager_limit %]">⇒</a> </form> </div> <script type="text/javascript"> $('#len').change(function(){$('#pager_limit_selection').submit()}); </script> <table class="fixed"> <caption>Users</caption> <thead> <tr> <th> <a href="?start=[% pager_start %];len=[% pager_limit %];orderby=id_user"> Id </a> </th> <th> <a href="?start=[% pager_start %];len=[% pager_limit %];orderby=username"> Username </a> </th> <th> <a href="?start=[% pager_start %];len=[% pager_limit %];orderby=realname"> Realname </a> </th> <th></th> </tr> </thead> <tbody> [%- FOREACH user = model.users %] [%- SET sub = user.active_subscription %] [%- SET pkg = sub.package %] <tr class="[% loop.parity %]"> <td>[% user.id_user %]</td> <td>[% user.username %]</td> <td>[% user.realname %]</td> <td><a href="[% SCRIPT_NAME %]/user/[% user.id_user %]">details</a></td> </tr> [%- END %] </tbody> </table>