1 package SL::Controller::Helper::Paginated;
5 use Exporter qw(import);
6 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);
8 use constant PRIV => '__paginatedhelper_priv';
10 my $controller_paginate_spec;
13 my ($class, %specs) = @_;
15 $specs{MODEL} ||= $class->controller_name;
16 $specs{MODEL} =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
17 $specs{PER_PAGE} ||= "SL::DB::Manager::$specs{MODEL}"->default_objects_per_page;
18 $specs{FORM_PARAMS} ||= [ qw(page per_page) ];
20 $specs{ONLY} = [ $specs{ONLY} ] if !ref $specs{ONLY};
21 $specs{ONLY_MAP} = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
23 $controller_paginate_spec = \%specs;
25 my %hook_params = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
26 $class->run_before('_save_current_paginate_params', %hook_params);
28 SL::Controller::Helper::GetModels::register_get_models_handlers(
30 callback => '_callback_handler_for_paginated',
31 get_models => '_get_models_handler_for_paginated',
35 # $::lxdebug->dump(0, "CONSPEC", \%specs);
38 sub get_paginate_spec {
39 my ($class_or_self) = @_;
41 return $controller_paginate_spec;
44 sub get_current_paginate_params {
45 my ($self, %params) = @_;
47 my $spec = $self->get_paginate_spec;
49 my $priv = _priv($self);
50 $params{page} = $priv->{page} unless defined $params{page};
51 $params{per_page} = $priv->{per_page} unless defined $params{per_page};
53 my %paginate_params = (
54 page => ($params{page} * 1) || 1,
55 per_page => ($params{per_page} * 1) || $spec->{PER_PAGE},
58 my %paginate_args = ref($spec->{PAGINATE_ARGS}) eq 'CODE' ? %{ $spec->{PAGINATE_ARGS}->($self) }
59 : $spec->{PAGINATE_ARGS} ? do { my $sub = $spec->{PAGINATE_ARGS}; %{ $self->$sub() } }
61 my $calculated_params = "SL::DB::Manager::$spec->{MODEL}"->paginate(%paginate_params, args => \%paginate_args);
64 num_pages => $calculated_params->{max},
65 common_pages => $calculated_params->{common},
68 # $::lxdebug->dump(0, "get_current_paginate_params: ", \%paginate_params);
70 return %paginate_params;
73 sub disable_pagination {
75 _priv($self)->{disabled} = 1;
82 sub _save_current_paginate_params {
85 return if !_is_enabled($self);
87 my $paginate_spec = $self->get_paginate_spec;
89 page => $::form->{ $paginate_spec->{FORM_PARAMS}->[0] } || 1,
90 per_page => $::form->{ $paginate_spec->{FORM_PARAMS}->[1] } * 1,
93 # $::lxdebug->message(0, "saving current paginate params to " . $self->{PRIV()}->{page} . ' / ' . $self->{PRIV()}->{per_page});
96 sub _callback_handler_for_paginated {
97 my ($self, %params) = @_;
98 my $priv = _priv($self);
100 if (_is_enabled($self) && $priv->{page}) {
101 my $paginate_spec = $self->get_paginate_spec;
102 $params{ $paginate_spec->{FORM_PARAMS}->[0] } = $priv->{page};
103 $params{ $paginate_spec->{FORM_PARAMS}->[1] } = $priv->{per_page} if $priv->{per_page};
106 # $::lxdebug->dump(0, "CB handler for paginated; params nach modif:", \%params);
111 sub _get_models_handler_for_paginated {
112 my ($self, %params) = @_;
113 my $spec = $self->get_paginate_spec;
114 $params{model} ||= $spec->{MODEL};
116 "SL::DB::Manager::$params{model}"->paginate($self->get_current_paginate_params, args => \%params) if _is_enabled($self);
118 # $::lxdebug->dump(0, "GM handler for paginated; params nach modif (is_enabled? " . _is_enabled($self) . ")", \%params);
125 $self->{PRIV()} ||= {};
126 return $self->{PRIV()};
131 return !_priv($self)->{disabled} && ($self->get_paginate_spec->{ONLY_MAP}->{$self->action_name} || $self->get_paginate_spec->{ONLY_MAP}->{'__ALL__'});
143 SL::Controller::Helper::Paginated - A helper for semi-automatic handling
144 of paginating lists of database models in a controller
150 use SL::Controller::Helper::GetModels;
151 use SL::Controller::Helper::Paginated;
153 __PACKAGE__->make_paginated(
154 MODEL => 'BackgroundJobHistory',
155 ONLY => [ qw(list) ],
156 FORM_PARAMS => [ qw(page per_page) ],
162 my $paginated_models = $self->get_models;
163 $self->render('controller/list', ENTRIES => $paginated_models);
178 [% FOREACH entry = ENTRIES %]
186 [% L.paginate_controls %]
190 This specialized helper module enables controllers to display a
191 paginatable list of database models with as few lines as possible. It
192 can also be combined trivially with the L<SL::Controller::Sorted>
193 helper for sortable lists.
195 For this to work the controller has to provide the information which
196 indexes are eligible for paginateing etc. by a call to
197 L<make_paginated> at compile time.
199 The underlying functionality that enables the use of more than just
200 the paginate helper is provided by the controller helper
201 C<GetModels>. See the documentation for L<SL::Controller::Sorted> for
202 more information on it.
204 A template can use the method C<paginate_controls> from the layout
205 helper module C<L> which renders the links for navigation between the
208 This module requires that the Rose model managers use their C<Paginated>
211 The C<Paginated> helper hooks into the controller call to the action via
212 a C<run_before> hook. This is done so that it can remember the paginate
213 parameters that were used in the current view.
215 =head1 PACKAGE FUNCTIONS
219 =item C<make_paginated %paginate_spec>
221 This function must be called by a controller at compile time. It is
222 uesd to set the various parameters required for this helper to do its
225 The hash C<%paginate_spec> can include the following parameters:
231 Optional. A string: the name of the Rose database model that is used
232 as a default in certain cases. If this parameter is missing then it is
233 derived from the controller's package (e.g. for the controller
234 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
235 C<BackgroundJobHistory>).
237 =item * C<PAGINATE_ARGS>
239 Optional. Either a code reference or the name of function to be called
240 on the controller importing this helper.
242 If this funciton is given then the paginate helper calls it whenever
243 it has to count the total number of models for calculating the number
244 of pages to display. The function must return a hash reference with
245 elements suitable for passing to a Rose model manager's C<get_all>
248 This can be used e.g. when filtering is used.
252 Optional. An integer: the number of models to return per page.
254 Defaults to the underlying database model's default number of models
257 =item * C<FORM_PARAMS>
259 Optional. An array reference with exactly two strings that name the
260 indexes in C<$::form> in which the current page's number (the first
261 element in the array) and the number of models per page (the second
262 element in the array) are stored.
264 Defaults to the values C<page> and C<per_page> if missing.
268 Optional. An array reference containing a list of action names for
269 which the paginate parameters should be saved. If missing or empty then
270 all actions invoked on the controller are monitored.
276 =head1 INSTANCE FUNCTIONS
278 These functions are called on a controller instance.
282 =item C<get_paginate_spec>
284 Returns a hash containing the currently active paginate
285 parameters. The following keys are returned:
291 The currently active page number (numbering starts at 1).
295 Number of models per page (at least 1).
299 Number of pages to display (at least 1).
301 =item * C<common_pages>
303 An array reference with one hash reference for each possible
304 page. Each hash ref contains the keys C<active> (C<1> if that page is
305 the currently active page), C<page> (the page number this hash
306 reference describes) and C<visible> (whether or not it should be
311 =item C<get_current_paginate_params>
313 Returns a hash reference to the paginate spec structure given in the call
314 to L<make_paginated> after normalization (hash reference construction,
315 applying default parameters etc).
317 =item C<disable_pagination>
319 Disable pagination for the duration of the current action. Can be used
320 when using the attribute C<ONLY> to L<make_paginated> does not
331 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>