Projekt

Allgemein

Profil

Herunterladen (10,6 KB) Statistiken
| Zweig: | Markierung: | Revision:
9deadd1d Moritz Bunkus
package SL::Controller::Helper::GetModels;

use strict;

ec3a4636 Sven Schöling
use parent 'Rose::Object';
use SL::Controller::Helper::GetModels::Filtered;
use SL::Controller::Helper::GetModels::Sorted;
use SL::Controller::Helper::GetModels::Paginated;

0f491583 Sven Schöling
use Scalar::Util qw(weaken);

ec3a4636 Sven Schöling
use Rose::Object::MakeMethods::Generic (
0f491583 Sven Schöling
scalar => [ qw(controller model query with_objects filtered sorted paginated finalized final_params) ],
0152cc2e Moritz Bunkus
'scalar --get_set_init' => [ qw(handlers source additional_url_params) ],
0f491583 Sven Schöling
array => [ qw(plugins) ],
ec3a4636 Sven Schöling
);
9deadd1d Moritz Bunkus
93f51d62 Moritz Bunkus
use constant PRIV => '__getmodelshelperpriv';

0f491583 Sven Schöling
# official interface

sub get {
my ($self) = @_;
my %params = $self->finalize;

return $self->manager->get_all(%params);
}

b300864d Sven Schöling
sub count {
my ($self) = @_;
my %params = $self->finalize;

return $self->manager->get_all_count(%params);
}

0f491583 Sven Schöling
sub disable_plugin {
my ($self, $plugin) = @_;
die 'cannot change internal state after finalize was called' if $self->finalized;
die 'unsupported plugin' unless $self->can($plugin) && $self->$plugin && $self->$plugin->isa('SL::Controller::Helper::GetModels::Base');
95f9f85a Sven Schöling
0f491583 Sven Schöling
$self->$plugin->disabled(1);
}

sub enable_plugin {
my ($self, $plugin) = @_;
die 'cannot change internal state after finalize was called' if $self->finalized;
die 'unsupported plugin' unless $self->can($plugin) && $self->$plugin && $self->$plugin->isa('SL::Controller::Helper::GetModels::Base');
$self->$plugin->disabled(0);
}

sub is_enabled_plugin {
my ($self, $plugin) = @_;
die 'unsupported plugin' unless $self->can($plugin) && $self->$plugin && $self->$plugin->isa('SL::Controller::Helper::GetModels::Base');
$self->$plugin->is_enabled;
}

# TODO: get better delegation
sub set_report_generator_sort_options {
my ($self, %params) = @_;
$self->finalize;

$self->sorted->set_report_generator_sort_options(%params);
}

sub get_paginate_args {
my ($self) = @_;
my %params = $self->finalize;

$self->paginated->get_current_paginate_params(%params);
}
ec3a4636 Sven Schöling
783342e0 Sven Schöling
sub get_sort_spec {
my ($self) = @_;

$self->sorted->specs;
}

sub get_current_sort_params {
my ($self) = @_;

$self->sorted->read_params;
}

ec3a4636 Sven Schöling
sub init {
my ($self, %params) = @_;

78bceada Sven Schöling
my $model = delete $params{model};
if (!$model && $params{controller} && ref $params{controller}) {
$model = ref $params{controller};
$model =~ s/.*:://;
die 'Need a valid model' unless $model;
}
$self->model($model);
ec3a4636 Sven Schöling
0f491583 Sven Schöling
my @plugins;
ec3a4636 Sven Schöling
for my $plugin (qw(filtered sorted paginated)) {
next unless my $spec = delete $params{$plugin} // {};
my $plugin_class = "SL::Controller::Helper::GetModels::" . ucfirst $plugin;
0f491583 Sven Schöling
push @plugins, $self->$plugin($plugin_class->new(%$spec, get_models => $self));
ec3a4636 Sven Schöling
}
0f491583 Sven Schöling
$self->plugins(@plugins);
ec3a4636 Sven Schöling
$self->SUPER::init(%params);
0f491583 Sven Schöling
$_->read_params for $self->plugins;

weaken $self->controller if $self->controller;
}

sub finalize {
my ($self, %params) = @_;

return %{ $self->final_params } if $self->finalized;

0152cc2e Moritz Bunkus
$self->register_handlers(callback => sub { shift; (@_, %{ $self->additional_url_params }) }) if %{ $self->additional_url_params };

0f491583 Sven Schöling
push @{ $params{query} ||= [] }, @{ $self->query || [] };
push @{ $params{with_objects} ||= [] }, @{ $self->with_objects || [] };

%params = $_->finalize(%params) for $self->plugins;

$self->finalized(1);
$self->final_params(\%params);

return %params;
ec3a4636 Sven Schöling
}
9deadd1d Moritz Bunkus
ec3a4636 Sven Schöling
sub register_handlers {
my ($self, %additional_handlers) = @_;
9deadd1d Moritz Bunkus
ec3a4636 Sven Schöling
my $handlers = $self->handlers;
33d5d38a Sven Schöling
map { push @{ $handlers->{$_} }, $additional_handlers{$_} if $additional_handlers{$_} } keys %$handlers;
9deadd1d Moritz Bunkus
}

0152cc2e Moritz Bunkus
sub add_additional_url_params {
my ($self, %params) = @_;

$self->additional_url_params({ %{ $self->additional_url_params }, %params });

return $self;
}

f40865cb Moritz Bunkus
sub get_models_url_params {
78bceada Sven Schöling
my ($self, $sub_name_or_code) = @_;
f40865cb Moritz Bunkus
78bceada Sven Schöling
my $code = (ref($sub_name_or_code) || '') eq 'CODE' ? $sub_name_or_code : sub { shift->controller->$sub_name_or_code(@_) };
f40865cb Moritz Bunkus
my $callback = sub {
my ($self, %params) = @_;
my @additional_params = $code->($self);
return (
%params,
(scalar(@additional_params) == 1) && (ref($additional_params[0]) eq 'HASH') ? %{ $additional_params[0] } : @additional_params,
);
};

9338cfe5 Sven Schöling
$self->register_handlers('callback' => $callback);
f40865cb Moritz Bunkus
}

9deadd1d Moritz Bunkus
sub get_callback {
my ($self, %override_params) = @_;

ec3a4636 Sven Schöling
my %default_params = $self->_run_handlers('callback', action => $self->controller->action_name);
9deadd1d Moritz Bunkus
ec3a4636 Sven Schöling
return $self->controller->url_for(%default_params, %override_params);
9deadd1d Moritz Bunkus
}

ec3a4636 Sven Schöling
sub manager {
die "No 'model' to work on" unless $_[0]->model;
"SL::DB::Manager::" . $_[0]->model;
9deadd1d Moritz Bunkus
}

#
# private/internal functions
#

sub _run_handlers {
my ($self, $handler_type, %params) = @_;

ec3a4636 Sven Schöling
foreach my $sub (@{ $self->handlers->{$handler_type} }) {
9deadd1d Moritz Bunkus
if (ref $sub eq 'CODE') {
%params = $sub->($self, %params);
} elsif ($self->can($sub)) {
%params = $self->$sub(%params);
} else {
die "SL::Controller::Helper::GetModels::get_callback: Cannot call $sub on " . ref($self) . ")";
}
}

return %params;
}

ec3a4636 Sven Schöling
sub init_handlers {
{
callback => [],
}
33d5d38a Sven Schöling
}

0f491583 Sven Schöling
sub init_source {
$::form
}

0152cc2e Moritz Bunkus
sub init_additional_url_params { +{} }

9deadd1d Moritz Bunkus
1;
__END__

=pod

=encoding utf8

=head1 NAME

1a8f793c Geoffrey Richardson
SL::Controller::Helper::GetModels - Base class for the GetModels system.
9deadd1d Moritz Bunkus
=head1 SYNOPSIS

78bceada Sven Schöling
In controller:
9deadd1d Moritz Bunkus
78bceada Sven Schöling
use SL::Controller::Helper::GetModels;

my $get_models = SL::Controller::Helper::GetModels->new(
controller => $self,
);
9deadd1d Moritz Bunkus
78bceada Sven Schöling
my $models = $self->get_models->get;

=head1 OVERVIEW
9deadd1d Moritz Bunkus
78bceada Sven Schöling
Building a CRUD controller would be easy, were it not for those stupid
1a8f793c Geoffrey Richardson
list actions. People unreasonably expect stuff like filtering, sorting,
78bceada Sven Schöling
paginating, exporting etc simply to work. Well, lets try to make it simply work
a little.
9deadd1d Moritz Bunkus
78bceada Sven Schöling
This class is a proxy between a controller and specialized
helper modules that handle these things (sorting, paginating etc) and gives you
the means to retrieve the information when needed to display sort headers or
paginating footers.
9deadd1d Moritz Bunkus
1a8f793c Geoffrey Richardson
Information about the requested data query can be stored in the object up to
78bceada Sven Schöling
a certain point, from which on the object becomes locked and can only be
4f949248 Sven Schöling
accessed for information. (See C<STATES>).
9deadd1d Moritz Bunkus
78bceada Sven Schöling
=head1 INTERFACE METHODS
9deadd1d Moritz Bunkus
=over 4

4f949248 Sven Schöling
=item new PARAMS
f40865cb Moritz Bunkus
78bceada Sven Schöling
Create a new GetModels object. Params must have at least an entry
C<controller>, other than that, see C<CONFIGURATION> for options.
f40865cb Moritz Bunkus
78bceada Sven Schöling
=item get

Retrieve all models for the current configuration. Will finalize the object.

=item get_models_url_params SUB

Register a sub to be called whenever an URL has to be generated (e.g. for sort
and pagination links). This is a way for the controller to add additional
parameters to the URL (e.g. for filter parameters).

The parameter can be either a code reference or the name of
f40865cb Moritz Bunkus
one of the controller's functions.

78bceada Sven Schöling
The value returned by C<SUB> must be either a single hash
f40865cb Moritz Bunkus
reference or a hash of key/value pairs to add to the URL.

0152cc2e Moritz Bunkus
=item add_additional_url_params C<%params>

Sets additional parameters that will be added to each URL generated by
this model (e.g. for pagination/sorting). This is just sugar for a
proper call to L<get_models_url_params> with an anonymous sub adding
those parameters.

78bceada Sven Schöling
=item get_callback

Returns a URL suitable for use as a callback parameter. It maps to the
current controller and action. All registered handlers of type
'callback' (e.g. the ones by C<Sorted> and C<Paginated>) can inject
the parameters they need so that the same list view as is currently
visible can be re-rendered.

Optional C<%params> passed to this function may override any parameter
set by the registered handlers.

=item enable_plugin PLUGIN

=item disable_plugin PLUGIN

=item is_enabled_plugin PLUGIN
9deadd1d Moritz Bunkus
78bceada Sven Schöling
Enable or disable the specified plugin. Useful to disable paginating for
exports for example. C<is_enabled_plugin> can be used to check the current
state fo a plugin.
9deadd1d Moritz Bunkus
78bceada Sven Schöling
Must not be finalized to use this.
9deadd1d Moritz Bunkus
78bceada Sven Schöling
=item finalize
9deadd1d Moritz Bunkus
78bceada Sven Schöling
Forces finalized state. Can be used on finalized objects without error.

Note that most higher functions will call this themselves to force a finalized
state. If you do use it it must come before any other finalizing methods, and
1a8f793c Geoffrey Richardson
will most likely function as a reminder for maintainers where your code
78bceada Sven Schöling
switches from configuration to finalized state.

=item source HASHREF

The source for user supplied information. Defaults to $::form. Changing it
after C<Base> phase has no effect.

=item controller CONTROLLER

A weakened link to the controller that created the GetModels object. Needed for
certain plugin methods.
9deadd1d Moritz Bunkus
=back

78bceada Sven Schöling
=head1 DELEGATION METHODS

4f949248 Sven Schöling
All of these finalize.

78bceada Sven Schöling
Methods delegating to C<Sorted>:
9deadd1d Moritz Bunkus
=over 4

78bceada Sven Schöling
=item *
9deadd1d Moritz Bunkus
78bceada Sven Schöling
set_report_generator_sort_options
9deadd1d Moritz Bunkus
78bceada Sven Schöling
=item *
9deadd1d Moritz Bunkus
78bceada Sven Schöling
get_sort_spec
9deadd1d Moritz Bunkus
78bceada Sven Schöling
=item *
9deadd1d Moritz Bunkus
78bceada Sven Schöling
get_current_sort_params

=back

Methods delegating to C<Paginated>:

=over 4

=item *
9deadd1d Moritz Bunkus
78bceada Sven Schöling
get_paginate_args
9deadd1d Moritz Bunkus
=back

78bceada Sven Schöling
=head1 STATES
9deadd1d Moritz Bunkus
78bceada Sven Schöling
A GetModels object is in one of 3 states at any given time. Their purpose is to
make a class of bugs impossible that orginated from changing the configuration
of a GetModels object halfway during the request. This was a huge problem in
the old implementation.
9deadd1d Moritz Bunkus
78bceada Sven Schöling
=over 4

=item Base

This is the state after creating a new object.

=item Init

1a8f793c Geoffrey Richardson
In this state all the information needed from the source ($::form) has been read
78bceada Sven Schöling
and subsequent changes to the source have no effect. In the current
4f949248 Sven Schöling
implementation this will happen during creation, so that the return value of
C<new> is already in state C<Init>.
78bceada Sven Schöling
=item Finalized

In this state no new configuration will be accepted so that information gotten
through the various methods is consistent. Every information retrieval method
4f949248 Sven Schöling
will trigger finalize.
78bceada Sven Schöling
=back


=head1 CONFIGURATION

Most of the configuration will be handed to GetModels on creation via C<new>.
This is a list of accepted params.

=over 4

=item controller SELF

The creating controller. Currently this is mandatory.

=item model MODEL

The name of the model for this GetModels instance. If none is given, the model
is inferred from the name of the controller class.

=item sorted PARAMS

=item paginated PARAMS

=item filtered PARAMS

Configuration for plugins. If the option for any plugin is omitted, it defaults
1a8f793c Geoffrey Richardson
to enabled and is configured by default. Giving a falsish value as first argument
78bceada Sven Schöling
will disable the plugin.

4f949248 Sven Schöling
If the value is a hashref, it will be passed to the plugin's C<init> method.
78bceada Sven Schöling
=item query

=item with_objects

Additional static parts for Rose to include into the final query.

=item source

Source for plugins to pull their data from. Defaults to $::form.

=back

=head1 BUGS AND CAVEATS

=over 4

=item *

Delegation is not as clean as it should be. Most of the methods rely on action
at a distance and should be moved out.

=back

=head1 AUTHORS
9deadd1d Moritz Bunkus
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>

78bceada Sven Schöling
Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>

9deadd1d Moritz Bunkus
=cut