1 package SL::Controller::Helper::GetModels::Sorted;
4 use parent 'SL::Controller::Helper::GetModels::Base';
7 use List::MoreUtils qw(uniq);
9 use Rose::Object::MakeMethods::Generic (
10 scalar => [ qw(by dir specs form_data) ],
11 'scalar --get_set_init' => [ qw(form_params) ],
15 my ($self, %specs) = @_;
17 $self->set_get_models(delete $specs{get_models});
18 my %model_sort_spec = $self->get_models->manager->_sort_spec;
20 if (my $default = delete $specs{_default}) {
21 $self->by ($default->{by});
22 $self->dir($default->{dir});
24 $self->by ($model_sort_spec{default}[0]);
25 $self->dir($model_sort_spec{default}[1]);
28 while (my ($column, $spec) = each %specs) {
29 next if $column =~ m/^[A-Z_]+$/;
31 $spec = $specs{$column} = { title => $spec } if (ref($spec) || '') ne 'HASH';
33 $spec->{model} ||= $self->get_models->model;
34 $spec->{model_column} ||= $column;
36 $self->specs(\%specs);
38 $self->get_models->register_handlers(
39 callback => sub { shift; $self->_callback_handler_for_sorted(@_) },
40 get_models => sub { shift; $self->_get_models_handler_for_sorted(@_) },
43 # $::lxdebug->dump(0, "CONSPEC", \%specs);
47 my ($self, %params) = @_;
49 return %{ $self->form_data } if $self->form_data;
52 my ($by, $dir) = @{ $self->form_params };
53 my $source = $self->get_models->source;
55 if ($source->{ $by }) {
57 sort_by => $source->{$by},
58 sort_dir => defined($source->{$dir}) ? $source->{$dir} * 1 : undef,
60 } elsif (!$self->by) {
61 %sort_params = %params;
65 sort_dir => $self->dir,
69 $self->form_data(\%sort_params);
75 my ($self, %params) = @_;
79 sub set_report_generator_sort_options {
80 my ($self, %params) = @_;
82 $params{$_} or croak("Missing parameter '$_'") for qw(report sortable_columns);
84 my %current_sort_params = $self->read_params;
86 foreach my $col (@{ $params{sortable_columns} }) {
87 $params{report}->{columns}->{$col}->{link} = $self->get_models->get_callback(
89 sort_dir => ($current_sort_params{sort_by} eq $col ? 1 - $current_sort_params{sort_dir} : $current_sort_params{sort_dir}),
93 $params{report}->set_sort_indicator($current_sort_params{sort_by}, 1 - $current_sort_params{sort_dir});
95 if ($params{report}->{export}) {
96 $params{report}->{export}->{variable_list} = [ uniq(
97 @{ $params{report}->{export}->{variable_list} },
98 @{ $self->form_params }
107 sub _callback_handler_for_sorted {
108 my ($self, %params) = @_;
109 my %spec = $self->read_params;
111 if ($spec{sort_by}) {
112 $params{ $self->form_params->[0] } = $spec{sort_by};
113 $params{ $self->form_params->[1] } = $spec{sort_dir};
116 # $::lxdebug->dump(0, "CB handler for sorted; params nach modif:", \%params);
121 sub _get_models_handler_for_sorted {
122 my ($self, %params) = @_;
124 my %sort_params = $self->read_params;
125 my $sort_spec = $self->specs->{ $sort_params{sort_by} };
127 $params{sort_by} = "SL::DB::Manager::$sort_spec->{model}"->make_sort_string(sort_by => $sort_spec->{model_column}, sort_dir => $sort_params{sort_dir});
129 # $::lxdebug->dump(0, "GM handler for sorted; params nach modif:", \%params);
135 sub init_form_params {
136 [ qw(sort_by sort_dir) ]
148 SL::Controller::Helper::Sorted - A helper for semi-automatic handling
149 of sorting lists of database models in a controller
155 use SL::Controller::Helper::GetModels;
156 use SL::Controller::Helper::Sorted;
158 __PACKAGE__->make_sorted(
159 DEFAULT_BY => 'run_at',
161 MODEL => 'BackgroundJobHistory',
162 ONLY => [ qw(list) ],
164 error => $::locale->text('Error'),
165 package_name => $::locale->text('Package name'),
166 run_at => $::locale->text('Run at'),
172 my $sorted_models = $self->get_models;
173 $self->render('controller/list', ENTRIES => $sorted_models);
182 <th>[% L.sortable_table_header('package_name') %]</th>
183 <th>[% L.sortable_table_header('run_at') %]</th>
184 <th>[% L.sortable_table_header('error') %]</th>
187 [% FOREACH entry = ENTRIES %]
189 <td>[% HTML.escape(entry.package_name) %]</td>
190 <td>[% HTML.escape(entry.run_at) %]</td>
191 <td>[% HTML.escape(entry.error) %]</td>
198 This specialized helper module enables controllers to display a
199 sortable list of database models with as few lines as possible.
201 For this to work the controller has to provide the information which
202 indexes are eligible for sorting etc. by a call to L<make_sorted> at
205 The underlying functionality that enables the use of more than just
206 the sort helper is provided by the controller helper C<GetModels>. It
207 provides mechanisms for helpers like this one to hook into certain
208 calls made by the controller (C<get_callback> and C<get_models>) so
209 that the specialized helpers can inject their parameters into the
210 calls to e.g. C<SL::DB::Manager::SomeModel::get_all>.
212 A template on the other hand can use the method
213 C<sortable_table_header> from the layout helper module C<L>.
215 This module requires that the Rose model managers use their C<Sorted>
218 The C<Sorted> helper hooks into the controller call to the action via
219 a C<run_before> hook. This is done so that it can remember the sort
220 parameters that were used in the current view.
222 =head1 PACKAGE FUNCTIONS
226 =item C<make_sorted %sort_spec>
228 This function must be called by a controller at compile time. It is
229 uesd to set the various parameters required for this helper to do its
232 There are two sorts of keys in the hash C<%sort_spec>. The first kind
233 is written in all upper-case. Those parameters are control
234 parameters. The second kind are all lower-case and represent indexes
235 that can be used for sorting (similar to database column names). The
236 second kind are also the indexes you use in a template when calling
237 C<[% L.sorted_table_header(...) %]>.
239 Control parameters include the following:
245 Optional. A string: the name of the Rose database model that is used
246 as a default in certain cases. If this parameter is missing then it is
247 derived from the controller's package (e.g. for the controller
248 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
249 C<BackgroundJobHistory>).
251 =item * C<DEFAULT_BY>
253 Optional. A string: the index to sort by if the user hasn't clicked on
254 any column yet (meaning: if the C<$::form> parameters for sorting do
255 not contain a valid index).
257 Defaults to the underlying database model's default sort column name.
259 =item * C<DEFAULT_DIR>
261 Optional. Default sort direction (ascending for trueish values,
262 descrending for falsish values).
264 Defaults to the underlying database model's default sort direction.
266 =item * C<FORM_PARAMS>
268 Optional. An array reference with exactly two strings that name the
269 indexes in C<$::form> in which the sort index (the first element in
270 the array) and sort direction (the second element in the array) are
273 Defaults to the values C<sort_by> and C<sort_dir> if missing.
277 Optional. An array reference containing a list of action names for
278 which the sort parameters should be saved. If missing or empty then
279 all actions invoked on the controller are monitored.
283 All keys that are written in all lower-case name indexes that can be
284 used for sorting. Each value to such a key can be either a string or a
285 hash reference containing certain elements. If the value is only a
286 string then such a hash reference is constructed, and the string is
287 used as the value for the C<title> key.
289 These possible elements are:
295 Required. A user-displayable title to be used by functions like the
296 layout helper's C<sortable_table_header>. Does not have a default
299 Note that this string must be the untranslated English version of the
300 string. The titles will be translated whenever they're requested.
304 Optional. The name of a Rose database model this sort index refers
305 to. If missing then the value of C<$sort_spec{MODEL}> is used.
307 =item * C<model_column>
309 Optional. The name of the Rose database model column this sort index
310 refers to. It must be one of the columns named by the model's
311 C<Sorted> helper (not to be confused with the controller's C<Sorted>
314 If missing it defaults to the key in C<%sort_spec> for which this hash
315 reference is the value.
321 =head1 INSTANCE FUNCTIONS
323 These functions are called on a controller instance.
327 =item C<get_sort_spec>
329 Returns a hash containing the currently active sort parameters.
331 The key C<by> contains the active sort index referring to the
332 C<%sort_spec> given to L<make_sorted>.
334 The key C<dir> is either C<1> or C<0>.
336 =item C<get_current_sort_params>
338 Returns a hash reference to the sort spec structure given in the call
339 to L<make_sorted> after normalization (hash reference construction,
340 applying default parameters etc).
342 =item C<set_report_generator_sort_options %params>
344 This function does three things with an instance of
345 L<SL::ReportGenerator>:
349 =item 1. it sets the sort indicator,
351 =item 2. it sets the the links for those column headers that are
354 =item 3. it adds the C<FORM_PARAMS> fields to the list of variables in
355 the report generator's export options.
359 The report generator instance must be passed as the parameter
360 C<report>. The parameter C<sortable_columns> must be an array
361 reference of column names that are sortable.
363 The report generator instance must already have its columns and export
364 options set via calls to its L<SL::ReportGenerator::set_columns> and
365 L<SL::ReportGenerator::set_export_options> functions.
375 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>