Projekt

Allgemein

Profil

Herunterladen (5,42 KB) Statistiken
| Zweig: | Markierung: | Revision:
41400107 Moritz Bunkus
package SL::Controller::Base;

use parent qw(Rose::Object);

use List::Util qw(first);

sub parse_html_template {
467d9029 Moritz Bunkus
my $self = shift;
my $name = shift;
my $locals = shift || {};
41400107 Moritz Bunkus
467d9029 Moritz Bunkus
return $::form->parse_html_template($name, { %{ $locals }, SELF => $self });
41400107 Moritz Bunkus
}

sub url_for {
my $self = shift;

1d9947e0 Moritz Bunkus
return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
41400107 Moritz Bunkus
1d9947e0 Moritz Bunkus
my %params = ref($_[0]) eq 'HASH' ? %{ $_[0] } : @_;
41400107 Moritz Bunkus
my $controller = delete($params{controller}) || $self->_controller_name;
my $action = delete($params{action}) || 'dispatch';
eb1efd21 Moritz Bunkus
$params{action} = "${controller}/${action}";
41400107 Moritz Bunkus
my $query = join('&', map { $::form->escape($_) . '=' . $::form->escape($params{$_}) } keys %params);

return "controller.pl?${query}";
}

sub _run_action {
my $self = shift;
my $action = "action_" . shift;

return $self->_dispatch(@_) if $action eq 'action_dispatch';

$::form->error("Invalid action ${action} for controller " . ref($self)) if !$self->can($action);
$self->$action(@_);
}

sub _controller_name {
return (split(/::/, ref($_[0])))[-1];
}

sub _dispatch {
my $self = shift;

my @actions = grep { m/^action_/ } keys %{ ref($self) . "::" };
my $action = first { $::form->{$_} } @actions;

$self->$action(@_);
}

1;
467d9029 Moritz Bunkus
__END__

=head1 NAME

SL::Controller::Base - base class for all action controllers

=head1 SYNOPSIS

=head2 OVERVIEW

This is a base class for all action controllers. Action controllers
provide subs that are callable by special URLs.

For each request made to the web server an instance of the controller
will be created. After the request has been served that instance will
handed over to garbage collection.

This base class is derived from L<Rose::Object>.

=head2 CONVENTIONS

The URLs have the following properties:

=over 2

=item *

The script part of the URL must be C<controller.pl>.

=item *

There must be a GET or POST parameter named C<action> containing the
name of the controller and the sub to call separated by C</>,
e.g. C<Message/list>.

=item *

The controller name is the package's name without the
C<SL::Controller::> prefix. At the moment only packages in the
C<SL::Controller> namespace are valid; sub-namespaces are not
allowed. The package name must start with an upper-case letter.

=item *

The sub part of the C<action> parameter is the name of the sub to
call. However, the sub's name is automatically prefixed with
C<action_>. Therefore for the example C<Message/list> the sub
C<SL::DB::Message::action_list> would be called. This in turn means
that subs whose name does not start with C<action_> cannot be invoked
directly via the URL.

=back

=head2 INDIRECT DISPATCHING

In the case that there are several submit buttons on a page it is
often impractical to have a single C<action> parameter match up
properly. For such a case a special dispatcher method is available. In
that case the C<action> parameter of the URL must be
C<Controller/dispatch>.

The C<SL::Controller::Base::_dispatch> method will iterate over all
subs in the controller package whose names start with C<action_>. The
first one for which there's a GET or POST parameter with the same name
and that's trueish is called.

Usage from a template usually looks like this:

<form method="POST" action="controller.pl">
...
<input type="hidden" name="action" value="Message/dispatch">
<input type="submit" name="action_mark_as_read" value="Mark messages as read">
<input type="submit" name="action_delete" value="Delete messages">
</form>

The dispatching is handled by the function L</_dispatch>.

=head1 FUNCTIONS

=head2 PUBLIC HELPER FUNCTIONS

These functions are supposed to be called by sub-classed controllers.

=over 4

=item C<parse_html_template $file_name, $local_variables>

Outputs an HTML template. It is a thin wrapper around
C<Form::parse_html_template> which also adds the current object as the
template variable C<SELF>.

=item C<url_for $url>

=item C<url_for $params>

=item C<url_for %params>

Creates an URL for the given parameters suitable for calling an action
controller. If there's only one scalar parameter then it is returned
verbatim.

Otherwise the parameters are given either as a single hash ref
parameter or as a normal hash.

The controller to call is given by C<$params{controller}>. It defaults
to the current controller as returned by
L</_controller_name>.

The action to call is given by C<$params{action}>. It defaults to
C<dispatch>.

All other key/value pairs in C<%params> are appended as GET parameters
to the URL.

Usage from a template might look like this:

<a href="[% SELF.url_for(controller => 'Message', action => 'new', recipient_id => 42) %]">create new message</a>

=back

=head2 PRIVATE FUNCTIONS

These functions are supposed to be used from this base class only.

=over 4

=item C<_controller_name>

Returns the name of the curernt controller package without the
C<SL::Controller::> prefix.

=item C<_dispatch>

Implements the method lookup for indirect dispatching mentioned in the
section L</INDIRECT DISPATCHING>.

=item C<_run_action $action>

Executes a sub based on the value of C<$action>. C<$action> is the sub
name part of the C<action> GET or POST parameter as described in
L</CONVENTIONS>.

If C<$action> equals C<dispatch> then the sub L</_dispatch> in this
base class is called for L</INDIRECT DISPATCHING>. Otherwise
C<$action> is prefixed with C<action_>, and that sub is called on the
current controller instance.

=back

=head1 AUTHOR

Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>

=cut