The Sysadmin Notebook  

Sitemap

Catalyst Basic Customisation

Adding Views and Modifying Controllers

Contents

Catalyst Controllers handle input parameters (user requests) and return the response to configured views. The view may be a template that processes data and returns the output in the appropriate format, which can be HTML, XML, JSON, a spreadsheet or any other format that you choose. The Template Toolkit provides a good starting point for creating views to return HTML output

The View

Top Bottom

To build the skeleton for a html view with TT use:

MyApp-Lite> perl script/myapp_lite_create.pl view Web TT

'Web' is the name of the perl module created in lib/MyApp/Lite/View/ directory and 'TT' is the type of view created. A test template is added to the 't/' directory called 'view_Web.t'

The Web.pm file doesn't initially contain much:

package MyApp::Lite::View::Web;

use strict;
use warnings;

use base 'Catalyst::View::TT';

__PACKAGE__->config(TEMPLATE_EXTENSION => '.tt');

1;

Using a TT view, catalyst controllers will by default look for templates in root/ that have the same name as the subroutine (action), with the value of the TEMPLATE_EXTENSION configuration variable appended. The path to the controller will be prepended to the path. Thus &index in the root controller will look for root/index.tt and the &piechart action in Controller::Reports will look for root/Reports/piechart.tt.

However the default Root controller will contain a response body, so to call the template instead the action must be removed, leaving the subroutine looking like this:

sub index :Path :Args(0) {
  my ($self, $c) = @_;
}

Now the index action in the Root controller will look for root/index.tt. A simple index.tt template might look like this:

[% WRAPPER page.tt title = c.config.name %]
<p>Hello World!</p>
[% END %]

and the wrapper template (page.tt) might look like this:

<!DOCTYPE html
	PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title>[% title %]</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>[% content %]</body>
</html>

The TTSite view can be used to create a more complete Template Toolkit setup for the views, adding footers, headers and other Template Toolkit magic to your templates

perl script/myapp_lite_create.pl view HTML TTSite

Don't forget to update HTML.pm with the TEMPLATE_EXTENSION value

The Controller

Top Bottom

The controller contains methods that interact with user input. In the example above, the controller does nothing more than render a view called index.tt. We can modify this template to accept and process some user input. So we add an input form to the template:

[% WRAPPER page.tt title = c.config.name %]
<form name="getUser" method=POST action="getUser">
  <input type="text" name="username" value="" title="enter your name here">
  <input type="submit" value="submit">
</form>
[% IF username %]
<p>Hello [% username %]!</p>
[% ELSE %]
<p>Hello World!</p>
[% END %]
[% END %]

The form has the action attribute set to getUser: this is name of the method that will be called in the controller. There is one input field called 'username'. Text in this field will be passed as an input parameter to the 'getUser' action in the controller when the submit button is clicked. So we modify the Root controller to handle this action:

sub getUser :Local {
	my ($self, $c) = @_;
	my $username = $c->req->body_params->{username};
	$c->stash(
		username => $username,
		template => 'index.tt',
	);
}

The getUser method collects the 'username' parameter in the form, adds it to the stash, and sets the value for the template to be rendered. If the template had not been set, catalyst would try to render a template called 'root/getUser.tt'. When the index.tt template is rendered, if username is defined, a personalised greeting is displayed. Otherwise, 'Hello World!' is displayed.

Input Parameters

Top Bottom

The catalyst request object has three idioms to collect parameters:

POST request
$c->req->body_params->{param}
GET request
$c->req->query_params->{param}
GET and POST requests
$c->req->params->{param}

The Model

Top Bottom

The Controller captures input data, the View presents the results of processing, and the Model contains the data-related functionality. Normally, this would be database functionality, processing input through database queries and returning result data back to the controller to hand-on to the correct view. The Model is called by the controller, which can pass any user input for processing.

We can create a skeleton model using:

MyApp-Lite> perl script/myapp_lite_create.pl model Transformer

this creates a Transformer.pm file in 'lib/MyApp/Lite/Model/':

package MyApp::Lite::Model::Transformer;

use strict;
use warnings;
use parent 'Catalyst::Model';

=head1 NAME

MyApp::Lite::Model::Transformer - Catalyst Model

=head1 DESCRIPTION

Catalyst Model.

=head1 AUTHOR

David Ramlakhan

=head1 LICENSE

This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

1;

To this module we can add our external models using various modules available on CPAN. For this example we'll process our input data through ACME::LeetSpeak by changing Transformer.pm as follows:

package MyApp::Lite::Model::Transformer;

use strict;
use warnings;
use parent 'Catalyst::Model';

use Acme::LeetSpeak ();

sub translate {
	my ($self, $text) = @_;
	return leet($text);
}

1;

Then we call the Model from our controller by changing the 'getUser' action in the Controller:

sub getUser :Local {
	my ($self, $c) = @_;
	my $username = $c->req->body_params->{username};
	$c->stash(
		username => $c->model('Transformer')->translate($username),
		template => 'index.tt',
	);
}

Having used the Acme::LeetSpeak module, we should remember to add this to the 'requires' section of the Makefile.PL

Models can also be created for database access. DBIx::Class provides methods to refers to relational databases as objects as perl objects. Drivers for your database will also need to be installed:

cpan DBIx::Class Catalyst::Model::DBIC::Schema

You will also need to install the DBD driver for your chosen database, which is usually available in rpm format. Don't forget to update Makefile.PL with these prerequisites.

Having installed the required database access modules, you can create the database model using:

perl script/sysmon_create.pl model Database \
DBIC::Schema Sysmon::Schema::Database create=static \
dbi:mysql:systems user password

Additional Views

Top Bottom

Our original view uses TT to present data in HTML format to the user. This is fine for a web application that the user accesses via a web browser. However Catalyst views exists for numerous output formats. These include views that present data in JSON, XML, PDF formats and views for downloading data. To create a new view that presents data in JSON format, run:

script/myapp_lite_create.pl view JSON JSON

With more than one view to choose from, we need to set the default view for our application in the application configuration file: myapp_lite.conf:

default_view Web

To use the new view, we can create an action in the Root controller:

sub JSON : Local {
	my ($self, $c) = @_;
	foreach my $param (keys %{$c->req->query_params}) {
		$c->stash->{$param} = $c->req->query_params->{$param};
	}
	$c->stash->{current_view} = 'JSON';
}

This action simply takes query requests parameters and returns them (via the JSON view) in JSON serialised format. The command:

 wget http://localhost:3000/JSON/index.html?size=Medium\&colour=blue

will return:

{"current_view":"JSON","colour":"blue","size":"Medium"}

Authentication

Top Bottom

Basic HTML authentication can be achieved by using the Catalyst::Helper::AuthDBIC. Once the module is installed, run the auth_bootstrap.pl script to create the authentication backend:

auth_bootstrap.pl -credential http

Then use the script/myapp_lite_auth_admin.pl to add a user:

script/myapp_lite_auth_admin.pl -user tester -password testing

Once the authentication mechanism has been installed and configured, add $c->authenticate to any controller action that requires authentication. To require authentication for the whole application add the following subroutine to the Root.pm controller:

sub auto : Private {
	my ($self, $c) = @_;
	$c->authenticate;
}