get_models callback entefrnt und in den finalize prozess integriert
[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
74   my %filter_params;
75   %filter_params = $self->read_params(%params)  if $self->is_enabled;
76
77   # $::lxdebug->dump(0, "GM handler for filtered; params nach modif (is_enabled? " . $self->is_enabled . ")", \%params);
78
79   return $self->merge_args(\%params, \%filter_params);
80 }
81
82 #
83 # private functions
84 #
85
86 sub _get_filter_args {
87   my ($self, $spec) = @_;
88
89   my %filter_args   = ref($self->filter_args) eq 'CODE' ? %{ $self->filter_args->($self) }
90                     :     $self->filter_args            ? do { my $sub = $self->filter_args; %{ $self->get_models->controller->$sub() } }
91                     :                                       ();
92 }
93
94 sub _callback_handler_for_filtered {
95   my ($self, %params) = @_;
96
97   if ($self->is_enabled) {
98     my ($flattened) = SL::Controller::Helper::ParseFilter::flatten($self->orig_filter, $self->form_params);
99     %params         = (%params, @{ $flattened || [] });
100   }
101
102   # $::lxdebug->dump(0, "CB handler for filtered; params after flatten:", \%params);
103
104   return %params;
105 }
106
107 sub init_form_params {
108   'filter'
109 }
110
111 sub init_launder_to {
112   'filter'
113 }
114
115
116 1;
117
118 __END__
119
120 =pod
121
122 =encoding utf8
123
124 =head1 NAME
125
126 SL::Controller::Helper::Filtered - A helper for semi-automatic handling
127 of filtered lists of database models in a controller
128
129 =head1 SYNOPSIS
130
131 In a controller:
132
133   use SL::Controller::Helper::GetModels;
134   use SL::Controller::Helper::Filtered;
135
136   __PACKAGE__->make_filter(
137     MODEL       => 'Part',
138     ONLY        => [ qw(list) ],
139     FORM_PARAMS => [ qw(filter) ],
140   );
141
142   sub action_list {
143     my ($self) = @_;
144
145     my $filtered_models = $self->get_models(%addition_filters);
146     $self->render('controller/list', ENTRIES => $filtered_models);
147   }
148
149
150 =head1 OVERVIEW
151
152 This helper module enables use of the L<SL::Controller::Helper::ParseFilter>
153 methods in conjunction with the L<SL::Controller::Helper::GetModels> style of
154 plugins. Additional filters can be defined in the database models and filtering
155 can be reduced to a minimum of work.
156
157 This plugin can be combined with L<SL::Controller::Sorted> and
158 L<SL::Controller::Paginated> for filtered, sorted and paginated lists.
159
160 The controller has to provive information where to look for filter information
161 at compile time. This call is L<make_filtered>.
162
163 The underlying functionality that enables the use of more than just
164 the paginate helper is provided by the controller helper
165 C<GetModels>. See the documentation for L<SL::Controller::Sorted> for
166 more information on it.
167
168 =head1 PACKAGE FUNCTIONS
169
170 =over 4
171
172 =item C<make_filtered %filter_spec>
173
174 This function must be called by a controller at compile time. It is
175 uesd to set the various parameters required for this helper to do its
176 magic.
177
178 Careful: If you want to use this in conjunction with
179 L<SL:Controller::Helper::Paginated>, you need to call C<make_filtered> first,
180 or the paginating will not get all the relevant information to estimate the
181 number of pages correctly. To ensure this does not happen, this module will
182 croak when it detects such a scenario.
183
184 The hash C<%filter_spec> can include the following parameters:
185
186 =over 4
187
188 =item * C<MODEL>
189
190 Optional. A string: the name of the Rose database model that is used
191 as a default in certain cases. If this parameter is missing then it is
192 derived from the controller's package (e.g. for the controller
193 C<SL::Controller::BackgroundJobHistory> the C<MODEL> would default to
194 C<BackgroundJobHistory>).
195
196 =item * C<FORM_PARAMS>
197
198 Optional. Indicates a key in C<$::form> to be used as filter.
199
200 Defaults to the values C<filter> if missing.
201
202 =item * C<LAUNDER_TO>
203
204 Option. Indicates a target for laundered filter arguments in the controller.
205 Can be set to C<undef> to disable laundering, and can be set to method named or
206 hash keys of the controller. In the latter case the laundered structure will be
207 put there.
208
209 Defaults to inplace laundering which is not normally settable.
210
211 =item * C<ONLY>
212
213 Optional. An array reference containing a list of action names for
214 which the paginate parameters should be saved. If missing or empty then
215 all actions invoked on the controller are monitored.
216
217 =back
218
219 =back
220
221 =head1 INSTANCE FUNCTIONS
222
223 These functions are called on a controller instance.
224
225 =over 4
226
227 =item C<get_current_filter_params>
228
229 Returns a hash to be used in manager C<get_all> calls or to be passed on to
230 GetModels. Will only work if the get_models chain has been called at least
231 once, because only then the full parameters can get parsed and stored. Will
232 croak otherwise.
233
234 =item C<disable_filtering>
235
236 Disable filtering for the duration of the current action. Can be used
237 when using the attribute C<ONLY> to L<make_filtered> does not
238 cover all cases.
239
240 =back
241
242 =head1 BUGS
243
244 Nothing here yet.
245
246 =head1 AUTHOR
247
248 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
249
250 =cut