Projekt

Allgemein

Profil

Herunterladen (9,16 KB) Statistiken
| Zweig: | Markierung: | Revision:
ef32afed Moritz Bunkus
package SL::Controller::Helper::Paginated;

use strict;

use Exporter qw(import);
5e45f456 Moritz Bunkus
our @EXPORT = qw(make_paginated get_paginate_spec get_current_paginate_params _save_current_paginate_params _get_models_handler_for_paginated _callback_handler_for_paginated disable_pagination);
ef32afed Moritz Bunkus
93f51d62 Moritz Bunkus
use constant PRIV => '__paginatedhelper_priv';

ccacef74 Moritz Bunkus
my %controller_paginate_spec;
ef32afed Moritz Bunkus
sub make_paginated {
my ($class, %specs) = @_;

29637d3f Moritz Bunkus
$specs{MODEL} ||= $class->controller_name;
ef32afed Moritz Bunkus
$specs{MODEL} =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
$specs{PER_PAGE} ||= "SL::DB::Manager::$specs{MODEL}"->default_objects_per_page;
$specs{FORM_PARAMS} ||= [ qw(page per_page) ];
$specs{ONLY} ||= [];
$specs{ONLY} = [ $specs{ONLY} ] if !ref $specs{ONLY};
5e45f456 Moritz Bunkus
$specs{ONLY_MAP} = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
ef32afed Moritz Bunkus
ccacef74 Moritz Bunkus
$controller_paginate_spec{$class} = \%specs;
ef32afed Moritz Bunkus
my %hook_params = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
$class->run_before('_save_current_paginate_params', %hook_params);

SL::Controller::Helper::GetModels::register_get_models_handlers(
$class,
callback => '_callback_handler_for_paginated',
get_models => '_get_models_handler_for_paginated',
ONLY => $specs{ONLY},
);

# $::lxdebug->dump(0, "CONSPEC", \%specs);
}

sub get_paginate_spec {
my ($class_or_self) = @_;

ccacef74 Moritz Bunkus
return $controller_paginate_spec{ref($class_or_self) || $class_or_self};
ef32afed Moritz Bunkus
}

sub get_current_paginate_params {
my ($self, %params) = @_;

my $spec = $self->get_paginate_spec;

5e45f456 Moritz Bunkus
my $priv = _priv($self);
93f51d62 Moritz Bunkus
$params{page} = $priv->{page} unless defined $params{page};
$params{per_page} = $priv->{per_page} unless defined $params{per_page};
ef32afed Moritz Bunkus
my %paginate_params = (
page => ($params{page} * 1) || 1,
per_page => ($params{per_page} * 1) || $spec->{PER_PAGE},
);

5e45f456 Moritz Bunkus
my %paginate_args = ref($spec->{PAGINATE_ARGS}) eq 'CODE' ? %{ $spec->{PAGINATE_ARGS}->($self) }
: $spec->{PAGINATE_ARGS} ? do { my $sub = $spec->{PAGINATE_ARGS}; %{ $self->$sub() } }
: ();
my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => \%paginate_args);
ef32afed Moritz Bunkus
%paginate_params = (
%paginate_params,
num_pages => $calculated_params->{max},
common_pages => $calculated_params->{common},
);

# $::lxdebug->dump(0, "get_current_paginate_params: ", \%paginate_params);

return %paginate_params;
}

5e45f456 Moritz Bunkus
sub disable_pagination {
my ($self) = @_;
_priv($self)->{disabled} = 1;
}

ef32afed Moritz Bunkus
#
# private functions
#

sub _save_current_paginate_params {
my ($self) = @_;

5e45f456 Moritz Bunkus
return if !_is_enabled($self);

ef32afed Moritz Bunkus
my $paginate_spec = $self->get_paginate_spec;
93f51d62 Moritz Bunkus
$self->{PRIV()} = {
page => $::form->{ $paginate_spec->{FORM_PARAMS}->[0] } || 1,
per_page => $::form->{ $paginate_spec->{FORM_PARAMS}->[1] } * 1,
};
ef32afed Moritz Bunkus
93f51d62 Moritz Bunkus
# $::lxdebug->message(0, "saving current paginate params to " . $self->{PRIV()}->{page} . ' / ' . $self->{PRIV()}->{per_page});
ef32afed Moritz Bunkus
}

sub _callback_handler_for_paginated {
my ($self, %params) = @_;
5e45f456 Moritz Bunkus
my $priv = _priv($self);
ef32afed Moritz Bunkus
5e45f456 Moritz Bunkus
if (_is_enabled($self) && $priv->{page}) {
ef32afed Moritz Bunkus
my $paginate_spec = $self->get_paginate_spec;
93f51d62 Moritz Bunkus
$params{ $paginate_spec->{FORM_PARAMS}->[0] } = $priv->{page};
$params{ $paginate_spec->{FORM_PARAMS}->[1] } = $priv->{per_page} if $priv->{per_page};
ef32afed Moritz Bunkus
}

# $::lxdebug->dump(0, "CB handler for paginated; params nach modif:", \%params);

return %params;
}

sub _get_models_handler_for_paginated {
my ($self, %params) = @_;
5e45f456 Moritz Bunkus
my $spec = $self->get_paginate_spec;
$params{model} ||= $spec->{MODEL};
ef32afed Moritz Bunkus
5e45f456 Moritz Bunkus
"SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params) if _is_enabled($self);
ef32afed Moritz Bunkus
5e45f456 Moritz Bunkus
# $::lxdebug->dump(0, "GM handler for paginated; params nach modif (is_enabled? " . _is_enabled($self) . ")", \%params);
ef32afed Moritz Bunkus
return %params;
}

5e45f456 Moritz Bunkus
sub _priv {
my ($self) = @_;
$self->{PRIV()} ||= {};
return $self->{PRIV()};
}

sub _is_enabled {
my ($self) = @_;
return !_priv($self)->{disabled} && ($self->get_paginate_spec->{ONLY_MAP}->{$self->action_name} || $self->get_paginate_spec->{ONLY_MAP}->{'__ALL__'});
}

ef32afed Moritz Bunkus
1;
__END__

=pod

=encoding utf8

=head1 NAME

SL::Controller::Helper::Paginated - A helper for semi-automatic handling
of paginating lists of database models in a controller

=head1 SYNOPSIS

In a controller:

use SL::Controller::Helper::GetModels;
use SL::Controller::Helper::Paginated;

__PACKAGE__->make_paginated(
MODEL => 'BackgroundJobHistory',
ONLY => [ qw(list) ],
FORM_PARAMS => [ qw(page per_page) ],
);

sub action_list {
my ($self) = @_;

my $paginated_models = $self->get_models;
$self->render('controller/list', ENTRIES => $paginated_models);
}

In said template:

[% USE L %]

<table>
<thead>
<tr>
...
</tr>
</thead>

<tbody>
[% FOREACH entry = ENTRIES %]
<tr>
...
</tr>
[% END %]
</tbody>
</table>

[% L.paginate_controls %]

=head1 OVERVIEW

This specialized helper module enables controllers to display a
paginatable list of database models with as few lines as possible. It
can also be combined trivially with the L<SL::Controller::Sorted>
helper for sortable lists.

For this to work the controller has to provide the information which
indexes are eligible for paginateing etc. by a call to
L<make_paginated> at compile time.

The underlying functionality that enables the use of more than just
the paginate helper is provided by the controller helper
C<GetModels>. See the documentation for L<SL::Controller::Sorted> for
more information on it.

A template can use the method C<paginate_controls> from the layout
helper module C<L> which renders the links for navigation between the
pages.

This module requires that the Rose model managers use their C<Paginated>
helper.

The C<Paginated> helper hooks into the controller call to the action via
a C<run_before> hook. This is done so that it can remember the paginate
parameters that were used in the current view.

=head1 PACKAGE FUNCTIONS

=over 4

=item C<make_paginated %paginate_spec>

This function must be called by a controller at compile time. It is
uesd to set the various parameters required for this helper to do its
magic.

The hash C<%paginate_spec> can include the following parameters:

=over 4

=item * C<MODEL>

Optional. A string: the name of the Rose database model that is used
as a default in certain cases. If this parameter is missing then it is
derived from the controller's package (e.g. for the controller
C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
C<BackgroundJobHistory>).

a99a31d6 Moritz Bunkus
=item * C<PAGINATE_ARGS>

Optional. Either a code reference or the name of function to be called
on the controller importing this helper.

If this funciton is given then the paginate helper calls it whenever
it has to count the total number of models for calculating the number
of pages to display. The function must return a hash reference with
elements suitable for passing to a Rose model manager's C<get_all>
function.

This can be used e.g. when filtering is used.

ef32afed Moritz Bunkus
=item * C<PER_PAGE>

Optional. An integer: the number of models to return per page.

Defaults to the underlying database model's default number of models
per page.

=item * C<FORM_PARAMS>

Optional. An array reference with exactly two strings that name the
indexes in C<$::form> in which the current page's number (the first
element in the array) and the number of models per page (the second
element in the array) are stored.

Defaults to the values C<page> and C<per_page> if missing.

=item * C<ONLY>

Optional. An array reference containing a list of action names for
which the paginate parameters should be saved. If missing or empty then
all actions invoked on the controller are monitored.

=back

=back

=head1 INSTANCE FUNCTIONS

These functions are called on a controller instance.

=over 4

=item C<get_paginate_spec>

Returns a hash containing the currently active paginate
parameters. The following keys are returned:

=over 4

=item * C<page>

The currently active page number (numbering starts at 1).

=item * C<per_page>

Number of models per page (at least 1).

=item * C<num_pages>

Number of pages to display (at least 1).

=item * C<common_pages>

An array reference with one hash reference for each possible
page. Each hash ref contains the keys C<active> (C<1> if that page is
the currently active page), C<page> (the page number this hash
reference describes) and C<visible> (whether or not it should be
displayed).

=back

=item C<get_current_paginate_params>

Returns a hash reference to the paginate spec structure given in the call
to L<make_paginated> after normalization (hash reference construction,
applying default parameters etc).

5e45f456 Moritz Bunkus
=item C<disable_pagination>

Disable pagination for the duration of the current action. Can be used
when using the attribute C<ONLY> to L<make_paginated> does not
cover all cases.

ef32afed Moritz Bunkus
=back

=head1 BUGS

Nothing here yet.

=head1 AUTHOR

Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>

=cut