weitere umstrukturierung
[kivitendo-erp.git] / SL / Controller / Helper / GetModels / Filtered.pm
1 package SL::Controller::Helper::GetModels::Filtered;
2
3 use strict;
4 use parent 'SL::Controller::Helper::GetModels::Base';
5
6 use Exporter qw(import);
7 use SL::Controller::Helper::ParseFilter ();
8 use List::MoreUtils qw(uniq);
9
10 use Rose::Object::MakeMethods::Generic (
11   scalar => [ qw(filter_args filter_params orig_filter) ],
12   'scalar --get_set_init' => [ qw(form_params launder_to) ],
13 );
14
15 sub init {
16   my ($self, %specs)             = @_;
17
18   $self->set_get_models(delete $specs{get_models});
19   $self->SUPER::init(%specs);
20
21   $self->get_models->register_handlers(
22     callback   => sub { shift; $self->_callback_handler_for_filtered(@_) },
23     get_models => sub { shift; $self->_get_models_handler_for_filtered(@_) },
24   );
25
26   # $::lxdebug->dump(0, "CONSPEC", \%specs);
27 }
28
29 sub read_params {
30   my ($self, %params)   = @_;
31
32   return %{ $self->filter_params } if $self->filter_params;
33   my $source = $self->get_models->source;
34
35   my $filter            = $params{filter} // $source->{ $self->form_params } // {};
36   $self->orig_filter($filter);
37
38   my %filter_args       = $self->_get_filter_args;
39   my %parse_filter_args = (
40     class        => $self->get_models->manager,
41     with_objects => $params{with_objects},
42   );
43   my $laundered;
44   if ($self->launder_to eq '__INPLACE__') {
45     # nothing to do
46   } elsif ($self->launder_to) {
47     $laundered = {};
48     $parse_filter_args{launder_to} = $laundered;
49   } else {
50     $parse_filter_args{no_launder} = 1;
51   }
52
53   my %calculated_params = SL::Controller::Helper::ParseFilter::parse_filter($filter, %parse_filter_args);
54   %calculated_params = $self->merge_args(\%calculated_params, \%filter_args, \%params);
55
56   if ($laundered) {
57     if ($self->get_models->controller->can($self->launder_to)) {
58       $self->get_models->controller->${\ $self->launder_to }($laundered);
59     } else {
60       $self->get_models->controller->{$self->launder_to} = $laundered;
61     }
62   }
63
64   # $::lxdebug->dump(0, "get_current_filter_params: ", \%calculated_params);
65
66   $self->filter_params(\%calculated_params);
67
68   return %calculated_params;
69 }
70
71 sub finalize {
72   my ($self, %params) = @_;
73   %params;
74 }
75
76 #
77 # private functions
78 #
79
80 sub _get_filter_args {
81   my ($self, $spec) = @_;
82
83   my %filter_args   = ref($self->filter_args) eq 'CODE' ? %{ $self->filter_args->($self) }
84                     :     $self->filter_args            ? do { my $sub = $self->filter_args; %{ $self->get_models->controller->$sub() } }
85                     :                                       ();
86 }
87
88 sub _callback_handler_for_filtered {
89   my ($self, %params) = @_;
90
91   if ($self->is_enabled) {
92     my ($flattened) = SL::Controller::Helper::ParseFilter::flatten($self->orig_filter, $self->form_params);
93     %params         = (%params, @{ $flattened || [] });
94   }
95
96   # $::lxdebug->dump(0, "CB handler for filtered; params after flatten:", \%params);
97
98   return %params;
99 }
100
101 sub _get_models_handler_for_filtered {
102   my ($self, %params)    = @_;
103
104   # $::lxdebug->dump(0,  "params in get_models_for_filtered", \%params);
105
106   my %filter_params;
107   %filter_params = $self->read_params(%params)  if $self->is_enabled;
108
109   # $::lxdebug->dump(0, "GM handler for filtered; params nach modif (is_enabled? " . $self->is_enabled . ")", \%params);
110
111   return $self->merge_args(\%params, \%filter_params);
112 }
113
114 sub init_form_params {
115   'filter'
116 }
117
118 sub init_launder_to {
119   'filter'
120 }
121
122
123 1;
124
125 __END__
126
127 =pod
128
129 =encoding utf8
130
131 =head1 NAME
132
133 SL::Controller::Helper::Filtered - A helper for semi-automatic handling
134 of filtered lists of database models in a controller
135
136 =head1 SYNOPSIS
137
138 In a controller:
139
140   use SL::Controller::Helper::GetModels;
141   use SL::Controller::Helper::Filtered;
142
143   __PACKAGE__->make_filter(
144     MODEL       => 'Part',
145     ONLY        => [ qw(list) ],
146     FORM_PARAMS => [ qw(filter) ],
147   );
148
149   sub action_list {
150     my ($self) = @_;
151
152     my $filtered_models = $self->get_models(%addition_filters);
153     $self->render('controller/list', ENTRIES => $filtered_models);
154   }
155
156
157 =head1 OVERVIEW
158
159 This helper module enables use of the L<SL::Controller::Helper::ParseFilter>
160 methods in conjunction with the L<SL::Controller::Helper::GetModels> style of
161 plugins. Additional filters can be defined in the database models and filtering
162 can be reduced to a minimum of work.
163
164 This plugin can be combined with L<SL::Controller::Sorted> and
165 L<SL::Controller::Paginated> for filtered, sorted and paginated lists.
166
167 The controller has to provive information where to look for filter information
168 at compile time. This call is L<make_filtered>.
169
170 The underlying functionality that enables the use of more than just
171 the paginate helper is provided by the controller helper
172 C<GetModels>. See the documentation for L<SL::Controller::Sorted> for
173 more information on it.
174
175 =head1 PACKAGE FUNCTIONS
176
177 =over 4
178
179 =item C<make_filtered %filter_spec>
180
181 This function must be called by a controller at compile time. It is
182 uesd to set the various parameters required for this helper to do its
183 magic.
184
185 Careful: If you want to use this in conjunction with
186 L<SL:Controller::Helper::Paginated>, you need to call C<make_filtered> first,
187 or the paginating will not get all the relevant information to estimate the
188 number of pages correctly. To ensure this does not happen, this module will
189 croak when it detects such a scenario.
190
191 The hash C<%filter_spec> can include the following parameters:
192
193 =over 4
194
195 =item * C<MODEL>
196
197 Optional. A string: the name of the Rose database model that is used
198 as a default in certain cases. If this parameter is missing then it is
199 derived from the controller's package (e.g. for the controller
200 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
201 C<BackgroundJobHistory>).
202
203 =item * C<FORM_PARAMS>
204
205 Optional. Indicates a key in C<$::form> to be used as filter.
206
207 Defaults to the values C<filter> if missing.
208
209 =item * C<LAUNDER_TO>
210
211 Option. Indicates a target for laundered filter arguments in the controller.
212 Can be set to C<undef> to disable laundering, and can be set to method named or
213 hash keys of the controller. In the latter case the laundered structure will be
214 put there.
215
216 Defaults to inplace laundering which is not normally settable.
217
218 =item * C<ONLY>
219
220 Optional. An array reference containing a list of action names for
221 which the paginate parameters should be saved. If missing or empty then
222 all actions invoked on the controller are monitored.
223
224 =back
225
226 =back
227
228 =head1 INSTANCE FUNCTIONS
229
230 These functions are called on a controller instance.
231
232 =over 4
233
234 =item C<get_current_filter_params>
235
236 Returns a hash to be used in manager C<get_all> calls or to be passed on to
237 GetModels. Will only work if the get_models chain has been called at least
238 once, because only then the full parameters can get parsed and stored. Will
239 croak otherwise.
240
241 =item C<disable_filtering>
242
243 Disable filtering for the duration of the current action. Can be used
244 when using the attribute C<ONLY> to L<make_filtered> does not
245 cover all cases.
246
247 =back
248
249 =head1 BUGS
250
251 Nothing here yet.
252
253 =head1 AUTHOR
254
255 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
256
257 =cut