Catalyst-Manual
view release on metacpan or search on metacpan
lib/Catalyst/Manual/Cookbook.pod view on Meta::CPAN
A graphical flowchart of how the dispatcher works can be found on the wiki at
L<https://web.archive.org/web/20190919010727/http://dev.catalystframework.org/attachment/wiki/WikiStart/catalyst-flow.png>.
=head2 DRY Controllers with Chained actions
Imagine that you would like the following paths in your application:
=over
=item B<< /cd/<ID>/track/<ID> >>
Displays info on a particular track.
In the case of a multi-volume CD, this is the track sequence.
=item B<< /cd/<ID>/volume/<ID>/track/<ID> >>
Displays info on a track on a specific volume.
=back
Here is some example code, showing how to do this with chained controllers:
package CD::Controller;
use base qw/Catalyst::Controller/;
sub root : Chained('/') PathPart('/cd') CaptureArgs(1) {
my ($self, $c, $cd_id) = @_;
$c->stash->{cd_id} = $cd_id;
$c->stash->{cd} = $self->model('CD')->find_by_id($cd_id);
}
sub trackinfo : Chained('track') PathPart('') Args(0) RenderView {
my ($self, $c) = @_;
}
package CD::Controller::ByTrackSeq;
use base qw/CD::Controller/;
sub track : Chained('root') PathPart('track') CaptureArgs(1) {
my ($self, $c, $track_seq) = @_;
$c->stash->{track} = $self->stash->{cd}->find_track_by_seq($track_seq);
}
package CD::Controller::ByTrackVolNo;
use base qw/CD::Controller/;
sub volume : Chained('root') PathPart('volume') CaptureArgs(1) {
my ($self, $c, $volume) = @_;
$c->stash->{volume} = $volume;
}
sub track : Chained('volume') PathPart('track') CaptureArgs(1) {
my ($self, $c, $track_no) = @_;
$c->stash->{track} = $self->stash->{cd}->find_track_by_vol_and_track_no(
$c->stash->{volume}, $track_no
);
}
Note that adding other actions (i.e. chain endpoints) which operate on a track
is simply a matter of adding a new sub to CD::Controller - no code is duplicated,
even though there are two different methods of looking up a track.
This technique can be expanded as needed to fulfil your requirements - for example,
if you inherit the first action of a chain from a base class, then mixing in a
different base class can be used to duplicate an entire URL hierarchy at a different
point within your application.
=head2 Component-based Subrequests
See L<Catalyst::Plugin::SubRequest>.
=head2 File uploads
=head3 Single file upload with Catalyst
To implement uploads in Catalyst, you need to have a HTML form similar to
this:
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="hidden" name="form_submit" value="yes">
<input type="file" name="my_file">
<input type="submit" value="Send">
</form>
It's very important not to forget C<enctype="multipart/form-data"> in
the form.
Catalyst Controller module 'upload' action:
sub upload : Global {
my ($self, $c) = @_;
if ( $c->request->parameters->{form_submit} eq 'yes' ) {
if ( my $upload = $c->request->upload('my_file') ) {
my $filename = $upload->filename;
my $target = "/tmp/upload/$filename";
unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
die( "Failed to copy '$filename' to '$target': $!" );
}
}
}
$c->stash->{template} = 'file_upload.html';
}
=head3 Multiple file upload with Catalyst
Code for uploading multiple files from one form needs a few changes:
The form should have this basic structure:
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="hidden" name="form_submit" value="yes">
<input type="file" name="file1" size="50"><br>
<input type="file" name="file2" size="50"><br>
<input type="file" name="file3" size="50"><br>
( run in 0.615 second using v1.01-cache-2.11-cpan-524268b4103 )