Erste Version GetModels rewrite
[kivitendo-erp.git] / SL / Controller / Helper / GetModels / Filtered.pm
diff --git a/SL/Controller/Helper/GetModels/Filtered.pm b/SL/Controller/Helper/GetModels/Filtered.pm
new file mode 100644 (file)
index 0000000..3374471
--- /dev/null
@@ -0,0 +1,279 @@
+package SL::Controller::Helper::GetModels::Filtered;
+
+use strict;
+use parent 'SL::Controller::Helper::GetModels::Base';
+
+use Exporter qw(import);
+use SL::Controller::Helper::ParseFilter ();
+use List::MoreUtils qw(uniq);
+
+use Rose::Object::MakeMethods::Generic (
+  scalar => [ qw(disabled filter_args filter_params) ],
+  'scalar --get_set_init' => [ qw(form_params launder_to) ],
+);
+
+sub init {
+  my ($self, %specs)             = @_;
+
+  $self->set_get_models(delete $specs{get_models});
+  $self->SUPER::init(%specs);
+
+  $self->get_models->register_handlers(
+    callback   => sub { shift; $self->_callback_handler_for_filtered(@_) },
+    get_models => sub { shift; $self->_get_models_handler_for_filtered(@_) },
+  );
+
+  # $::lxdebug->dump(0, "CONSPEC", \%specs);
+}
+
+sub get_current_filter_params {
+  my ($self)   = @_;
+
+  return $self->filter_params if $self->filter_params;
+
+  require Carp;
+  Carp::confess('It seems a GetModels plugin tries to access filter params before they got calculated. Make sure your make_filtered call comes first.');
+}
+
+sub _make_current_filter_params {
+  my ($self, %params)   = @_;
+
+#  my $spec              = $self->get_filter_spec;
+  my $filter            = $params{filter} // $::form->{ $self->form_params } // {},
+  my %filter_args       = $self->_get_filter_args;
+  my %parse_filter_args = (
+    class        => $self->get_models->manager,
+    with_objects => $params{with_objects},
+  );
+  my $laundered;
+  if ($self->launder_to eq '__INPLACE__') {
+    # nothing to do
+  } elsif ($self->launder_to) {
+    $laundered = {};
+    $parse_filter_args{launder_to} = $laundered;
+  } else {
+    $parse_filter_args{no_launder} = 1;
+  }
+
+  my %calculated_params = SL::Controller::Helper::ParseFilter::parse_filter($filter, %parse_filter_args);
+  %calculated_params = $self->merge_args(\%calculated_params, \%filter_args, \%params);
+
+#  $calculated_params{query} = [
+#    @{ $calculated_params{query} || [] },
+#    @{ $filter_args{      query} || [] },
+#    @{ $params{           query} || [] },
+#  ];
+#
+#  $calculated_params{with_objects} = [
+#    uniq
+#    @{ $calculated_params{with_objects} || [] },
+#    @{ $filter_args{      with_objects} || [] },
+#    @{ $params{           with_objects} || [] },
+#  ];
+
+  if ($laundered) {
+    if ($self->get_models->controller->can($self->launder_to)) {
+      $self->get_models->controller->${\ $self->launder_to }($laundered);
+    } else {
+      $self->get_models->controller->{$self->launder_to} = $laundered;
+    }
+  }
+
+  # $::lxdebug->dump(0, "get_current_filter_params: ", \%calculated_params);
+
+  $self->filter_params(\%calculated_params);
+
+  return %calculated_params;
+}
+
+sub disable_filtering {
+  my ($self)               = @_;
+  $self->disabled(1);
+}
+
+#
+# private functions
+#
+
+sub _get_filter_args {
+  my ($self, $spec) = @_;
+
+  my %filter_args   = ref($self->filter_args) eq 'CODE' ? %{ $self->filter_args->($self) }
+                    :     $self->filter_args            ? do { my $sub = $self->filter_args; %{ $self->get_models->controller->$sub() } }
+                    :                                       ();
+}
+
+sub _callback_handler_for_filtered {
+  my ($self, %params) = @_;
+
+  if ($self->is_enabled) {
+    my ($flattened) = SL::Controller::Helper::ParseFilter::flatten($::form->{ $self->form_params }, $self->form_params);
+    %params         = (%params, @{ $flattened || [] });
+  }
+
+  # $::lxdebug->dump(0, "CB handler for filtered; params after flatten:", \%params);
+
+  return %params;
+}
+
+sub _get_models_handler_for_filtered {
+  my ($self, %params)    = @_;
+
+  # $::lxdebug->dump(0,  "params in get_models_for_filtered", \%params);
+
+  my %filter_params;
+  %filter_params = $self->_make_current_filter_params(%params)  if $self->is_enabled;
+
+  # $::lxdebug->dump(0, "GM handler for filtered; params nach modif (is_enabled? " . $self->is_enabled . ")", \%params);
+
+  return (%params, %filter_params);
+}
+
+sub is_enabled {
+  !$_[0]->disabled;
+}
+
+sub init_form_params {
+  'filter'
+}
+
+sub init_launder_to {
+  'filter'
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Controller::Helper::Filtered - A helper for semi-automatic handling
+of filtered lists of database models in a controller
+
+=head1 SYNOPSIS
+
+In a controller:
+
+  use SL::Controller::Helper::GetModels;
+  use SL::Controller::Helper::Filtered;
+
+  __PACKAGE__->make_filter(
+    MODEL       => 'Part',
+    ONLY        => [ qw(list) ],
+    FORM_PARAMS => [ qw(filter) ],
+  );
+
+  sub action_list {
+    my ($self) = @_;
+
+    my $filtered_models = $self->get_models(%addition_filters);
+    $self->render('controller/list', ENTRIES => $filtered_models);
+  }
+
+
+=head1 OVERVIEW
+
+This helper module enables use of the L<SL::Controller::Helper::ParseFilter>
+methods in conjunction with the L<SL::Controller::Helper::GetModels> style of
+plugins. Additional filters can be defined in the database models and filtering
+can be reduced to a minimum of work.
+
+This plugin can be combined with L<SL::Controller::Sorted> and
+L<SL::Controller::Paginated> for filtered, sorted and paginated lists.
+
+The controller has to provive information where to look for filter information
+at compile time. This call is L<make_filtered>.
+
+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.
+
+=head1 PACKAGE FUNCTIONS
+
+=over 4
+
+=item C<make_filtered %filter_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.
+
+Careful: If you want to use this in conjunction with
+L<SL:Controller::Helper::Paginated>, you need to call C<make_filtered> first,
+or the paginating will not get all the relevant information to estimate the
+number of pages correctly. To ensure this does not happen, this module will
+croak when it detects such a scenario.
+
+The hash C<%filter_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>).
+
+=item * C<FORM_PARAMS>
+
+Optional. Indicates a key in C<$::form> to be used as filter.
+
+Defaults to the values C<filter> if missing.
+
+=item * C<LAUNDER_TO>
+
+Option. Indicates a target for laundered filter arguments in the controller.
+Can be set to C<undef> to disable laundering, and can be set to method named or
+hash keys of the controller. In the latter case the laundered structure will be
+put there.
+
+Defaults to inplace laundering which is not normally settable.
+
+=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_current_filter_params>
+
+Returns a hash to be used in manager C<get_all> calls or to be passed on to
+GetModels. Will only work if the get_models chain has been called at least
+once, because only then the full parameters can get parsed and stored. Will
+croak otherwise.
+
+=item C<disable_filtering>
+
+Disable filtering for the duration of the current action. Can be used
+when using the attribute C<ONLY> to L<make_filtered> does not
+cover all cases.
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut