1Catalyst::Manual::TutorUisaelr:C:Ca0ot3na_tlMryoisrbteu:Ct:aeMtdaanlPuyeasrltl:B:aDTsouictcuosmr(ei3na)tla:t:i0o3n_MoreCatalystBasics(3)
2
3
4
6 Catalyst::Manual::Tutorial::03_MoreCatalystBasics - Catalyst Tutorial -
7 Chapter 3: More Catalyst Application Development Basics
8
10 This is Chapter 3 of 10 for the Catalyst tutorial.
11
12 Tutorial Overview
13
14 1. Introduction
15
16 2. Catalyst Basics
17
18 3. 03_More Catalyst Basics
19
20 4. Basic CRUD
21
22 5. Authentication
23
24 6. Authorization
25
26 7. Debugging
27
28 8. Testing
29
30 9. Advanced CRUD
31
32 10. Appendices
33
35 This chapter of the tutorial builds on the work done in Chapter 2 to
36 explore some features that are more typical of "real world" web
37 applications. From this chapter of the tutorial onward, we will be
38 building a simple book database application. Although the application
39 will be too limited to be of use to anyone, it should provide a basic
40 environment where we can explore a variety of features used in
41 virtually all web applications.
42
43 You can check out the source code for this example from the Catalyst
44 Subversion repository as per the instructions in
45 Catalyst::Manual::Tutorial::01_Intro.
46
47 Please take a look at "CATALYST INSTALLATION" in
48 Catalyst::Manual::Tutorial::01_Intro before doing the rest of this
49 tutorial. Although the tutorial should work correctly under most any
50 recent version of Perl running on any operating system, the tutorial
51 has been written using Debian 5 and tested to be sure it runs correctly
52 in this environment.
53
55 The remainder of the tutorial will build an application called "MyApp".
56 First use the Catalyst "catalyst.pl" script to initialize the framework
57 for the "MyApp" application (make sure you aren't still inside the
58 directory of the "Hello" application from the previous chapter of the
59 tutorial or in a directory that already has a "MyApp" subdirectory):
60
61 $ catalyst.pl MyApp
62 created "MyApp"
63 created "MyApp/script"
64 created "MyApp/lib"
65 created "MyApp/root"
66 ...
67 created "MyApp/script/myapp_create.pl"
68 Change to application directory and Run "perl Makefile.PL" to make sure your install is complete
69 $ cd MyApp
70
71 This creates a similar skeletal structure to what we saw in Chapter 2
72 of the tutorial, except with "MyApp" and "myapp" substituted for
73 "Hello" and "hello". (As noted in Chapter 2, omit the ".pl" from the
74 command if you are using Strawberry Perl.)
75
77 One of the greatest benefits of Catalyst is that it has such a large
78 library of bases classes and plugins available that you can use easily
79 add functionality to your application. Plugins are used to seamlessly
80 integrate existing Perl modules into the overall Catalyst framework. In
81 general, they do this by adding additional methods to the "context"
82 object (generally written as $c) that Catalyst passes to every
83 component throughout the framework.
84
85 By default, Catalyst enables three plugins/flags:
86
87 · "-Debug" Flag
88
89 Enables the Catalyst debug output you saw when we started the
90 "script/myapp_server.pl" development server earlier. You can
91 remove this item when you place your application into production.
92
93 To be technically correct, it turns out that "-Debug" is not a
94 plugin, but a flag. Although most of the items specified on the
95 "__PACKAGE__->setup" line of your application class will be
96 plugins, Catalyst supports a limited number of flag options (of
97 these, "-Debug" is the most common). See the documentation for
98 "Catalyst.pm" to get details on other flags (currently "-Engine",
99 "-Home", and "-Log").
100
101 If you prefer, there are several other ways to enable debug output:
102
103 · Use the "$c->debug" method
104
105 · The "-d" option to "script/myapp_server.pl"
106
107 · The "CATALYST_DEBUG=1" environment variable (or set it to zero
108 to templorarily disable debug output).
109
110 TIP: Depending on your needs, it can be helpful to permanently
111 remove "-Debug" from "lib/MyApp.pm" and then use the "-d" option to
112 "script/myapp_server.pl" to re-enable it just for the development
113 server. We will not be using that approach in the tutorial, but
114 feel free to make use of it in your own projects.
115
116 · Catalyst::Plugin::ConfigLoader
117
118 "ConfigLoader" provides an automatic way to load configurable
119 parameters for your application from a central Config::General file
120 (versus having the values hard-coded inside your Perl modules).
121 Config::General uses syntax very similar to Apache configuration
122 files. We will see how to use this feature of Catalyst during the
123 authentication and authorization sections (Chapter 5 and Chapter
124 6).
125
126 IMPORTANT NOTE: If you are using a version of Catalyst::Devel prior
127 to version 1.06, be aware that Catalyst changed the default format
128 from YAML to the more straightforward "Config::General" style.
129 This tutorial uses the newer "myapp.conf" file for
130 "Config::General". However, Catalyst supports both formats and will
131 automatically use either "myapp.conf" or "myapp.yml" (or any other
132 format supported by Catalyst::Plugin::ConfigLoader and
133 Config::Any). If you are using a version of Catalyst::Devel prior
134 to 1.06, you can convert to the newer format by simply creating the
135 "myapp.conf" file manually and deleting "myapp.yml". The default
136 contents of the "myapp.conf" you create should only consist of one
137 line:
138
139 name MyApp
140
141 TIP: This script can be useful for converting between configuration
142 formats:
143
144 perl -Ilib -e 'use MyApp; use Config::General;
145 Config::General->new->save_file("myapp.conf", MyApp->config);'
146
147 · Catalyst::Plugin::Static::Simple
148
149 "Static::Simple" provides an easy way to serve static content, such
150 as images and CSS files, from the development server.
151
152 For our application, we want to add one new plugin into the mix. To do
153 this, edit "lib/MyApp.pm" (this file is generally referred to as your
154 application class) and delete the lines with:
155
156 use Catalyst qw/
157 -Debug
158 ConfigLoader
159 Static::Simple
160 /;
161
162 Then replace it with:
163
164 # Load plugins
165 use Catalyst qw/
166 -Debug
167 ConfigLoader
168 Static::Simple
169
170 StackTrace
171 /;
172
173 Note: Recent versions of "Catalyst::Devel" have used a variety of
174 techniques to load these plugins/flags. For example, you might see the
175 following:
176
177 __PACKAGE__->setup(qw/-Debug ConfigLoader Static::Simple/);
178
179 Don't let these variations confuse you -- they all accomplish the same
180 result.
181
182 This tells Catalyst to start using one additional plugin,
183 Catalyst::Plugin::StackTrace, to add a stack trace to the standard
184 Catalyst "debug screen" (the screen Catalyst sends to your browser when
185 an error occurs). Be aware that StackTrace output appears in your
186 browser, not in the console window from which you're running your
187 application, which is where logging output usually goes.
188
189 Make sure when adding new plugins you also include them as a new
190 dependency within the Makefile.PL file. For example, after adding the
191 StackTrace plugin the Makefile.PL should include the following line:
192
193 requires 'Catalyst::Plugin::StackTrace';
194
195 Notes:
196
197 · "__PACKAGE__" is just a shorthand way of referencing the name of
198 the package where it is used. Therefore, in "MyApp.pm",
199 "__PACKAGE__" is equivalent to "MyApp".
200
201 · You will want to disable StackTrace before you put your application
202 into production, but it can be helpful during development.
203
204 · When specifying plugins, you can omit "Catalyst::Plugin::" from the
205 name. Additionally, you can spread the plugin names across
206 multiple lines as shown here or place them all on one line.
207
209 As discussed earlier, controllers are where you write methods that
210 interact with user input. Typically, controller methods respond to
211 "GET" and "POST" requests from the user's web browser.
212
213 Use the Catalyst "create" script to add a controller for book-related
214 actions:
215
216 $ script/myapp_create.pl controller Books
217 exists "/home/me/MyApp/script/../lib/MyApp/Controller"
218 exists "/home/me/MyApp/script/../t"
219 created "/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm"
220 created "/home/me/MyApp/script/../t/controller_Books.t"
221
222 Then edit "lib/MyApp/Controller/Books.pm" (as discussed in Chapter 2 of
223 the Tutorial, Catalyst has a separate directory under "lib/MyApp" for
224 each of the three parts of MVC: "Model", "View", and "Controller") and
225 add the following method to the controller:
226
227 =head2 list
228
229 Fetch all book objects and pass to books/list.tt2 in stash to be displayed
230
231 =cut
232
233 sub list :Local {
234 # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
235 # 'Context' that's used to 'glue together' the various components
236 # that make up the application
237 my ($self, $c) = @_;
238
239 # Retrieve all of the book records as book model objects and store in the
240 # stash where they can be accessed by the TT template
241 # $c->stash(books => [$c->model('DB::Book')->all]);
242 # But, for now, use this code until we create the model later
243 $c->stash(books => '');
244
245 # Set the TT template to use. You will almost always want to do this
246 # in your action methods (action methods respond to user input in
247 # your controllers).
248 $c->stash(template => 'books/list.tt2');
249 }
250
251 TIP: See Appendix 1 for tips on removing the leading spaces when
252 cutting and pasting example code from POD-based documents.
253
254 Programmers experienced with object-oriented Perl should recognize
255 $self as a reference to the object where this method was called. On
256 the other hand, $c will be new to many Perl programmers who have not
257 used Catalyst before (it's sometimes written as $context). The Context
258 object is automatically passed to all Catalyst components. It is used
259 to pass information between components and provide access to Catalyst
260 and plugin functionality.
261
262 Catalyst actions are regular Perl methods, but they make use of
263 attributes (the "":Local"" next to the ""sub list"" in the code above)
264 to provide additional information to the Catalyst dispatcher logic
265 (note that the space between the colon and the attribute name is
266 optional; you will see attributes written both ways). Most Catalyst
267 Controllers use one of five action types:
268
269 · :Private -- Use ":Private" for methods that you want to make into
270 an action, but you do not want Catalyst to directly expose the
271 method to your users. Catalyst will not map ":Private" methods to
272 a URI. Use them for various sorts of "special" methods (the
273 "begin", "auto", etc. discussed below) or for methods you want to
274 be able to "forward" or "detach" to. (If the method is a plain old
275 "helper method" that you don't want to be an action at all, then
276 just define the method without any attribute -- you can call it in
277 your code, but the Catalyst dispatcher will ignore it.)
278
279 There are five types of "special" build-in ":Private" actions:
280 "begin", "end", "default", "index", and "auto".
281
282 · With "begin", "end", "default", "index" private actions, only
283 the most specific action of each type will be called. For
284 example, if you define a "begin" action in your controller it
285 will override a "begin" action in your application/root
286 controller -- only the action in your controller will be
287 called.
288
289 · Unlike the other actions where only a single method is called
290 for each request, every auto action along the chain of
291 namespaces will be called. Each "auto" action will be called
292 from the application/root controller down through the most
293 specific class.
294
295 · :Path -- ":Path" actions let you map a method to an explicit URI
296 path. For example, "":Path('list')"" in
297 "lib/MyApp/Controller/Books.pm" would match on the URL
298 "http://localhost:3000/books/list", but "":Path('/list')"" would
299 match on "http://localhost:3000/list" (because of the leading
300 slash). You can use ":Args()" to specify how many arguments an
301 action should accept. See "Action_types" in
302 Catalyst::Manual::Intro for more information and examples.
303
304 · :Local -- ":Local" is merely a shorthand for
305 "":Path('_name_of_method_')"". For example, these are equivalent:
306 ""sub create_book :Local {...}"" and ""sub create_book
307 :Path('create_book') {...}"".
308
309 · :Global -- ":Global" is merely a shorthand for
310 "":Path('/_name_of_method_')"". For example, these are equivalent:
311 ""sub create_book :Global {...}"" and ""sub create_book
312 :Path('/create_book') {...}"".
313
314 · :Chained -- Newer Catalyst applications tend to use the Chained
315 dispatch form of action types because of its power and flexibility.
316 It allows a series of controller methods to be automatically
317 dispatched to service a single user request. See
318 Catalyst::Manual::Tutorial::04_BasicCRUD and
319 Catalyst::DispatchType::Chained for more information on chained
320 actions.
321
322 You should refer to "Action_types" in Catalyst::Manual::Intro for
323 additional information and for coverage of some lesser-used action
324 types not discussed here ("Regex" and "LocalRegex").
325
327 As mentioned in Chapter 2 of the tutorial, views are where you render
328 output, typically for display in the user's web browser (but also
329 possibly using into output-generation systems, such as PDF or JSON).
330 The code in "lib/MyApp/View" selects the type of view to use, with the
331 actual rendering template found in the "root" directory. As with
332 virtually every aspect of Catalyst, options abound when it comes to the
333 specific view technology you adopt inside your application. However,
334 most Catalyst applications use the Template Toolkit, known as TT (for
335 more information on TT, see http://www.template-toolkit.org
336 <http://www.template-toolkit.org>). Other somewhat popular view
337 technologies include Mason (<http://www.masonhq.com> and
338 <http://www.masonbook.com>) and HTML::Template
339 (http://html-template.sourceforge.net <http://html-
340 template.sourceforge.net>).
341
342 Create a Catalyst View
343 When using TT for the Catalyst view, the main helper script is
344 Catalyst::Helper::View::TT. You may also come across references to
345 Catalyst::Helper::View::TTSite, but its use is now deprecated.
346
347 Enter the following command to enable the "TT" style of view rendering
348 for this tutorial:
349
350 $ script/myapp_create.pl view TT TT
351 exists "/home/me/MyApp/script/../lib/MyApp/View"
352 exists "/home/me/MyApp/script/../t"
353 created "/home/me/MyApp/script/../lib/MyApp/View/TT.pm"
354 created "/home/me/MyApp/script/../t/view_TT.t"
355
356 This simply creates a view called "TT" (the second 'TT' argument) in a
357 file called "TT.pm" (the first 'TT' argument). It is now up to you to
358 decide how you want to structure your view layout. For the tutorial,
359 we will start with a very simple TT template to initially demonstrate
360 the concepts, but quickly migrate to a more typical "wrapper page" type
361 of configuration (where the "wrapper" controls the overall "look and
362 feel" of your site from a single file or set of files).
363
364 Edit "lib/MyApp/View/TT.pm" and you should see that the default
365 contents contains something similar to the following:
366
367 __PACKAGE__->config(TEMPLATE_EXTENSION => '.tt');
368
369 And update it to match:
370
371 __PACKAGE__->config(
372 # Change default TT extension
373 TEMPLATE_EXTENSION => '.tt2',
374 # Set the location for TT files
375 INCLUDE_PATH => [
376 MyApp->path_to( 'root', 'src' ),
377 ],
378 );
379
380 NOTE: Make sure to add a comma after '.tt2' outside the single quote.
381
382 This changes the default extension for Template Toolkit from '.tt' to
383 '.tt2' and changes the base directory for your template files from
384 "root" to "root/src". Stick with these conventions for the tutorial,
385 but feel free to use whatever options you desire in your applications
386 (as with most things Perl, there's more than one way to do it...).
387
388 Note: We will use "root/src" as the base directory for our template
389 files, with a full naming convention of
390 "root/src/_controller_name_/_action_name_.tt2". Another popular option
391 is to use "root/" as the base (with a full filename pattern of
392 "root/_controller_name_/_action_name_.tt2").
393
394 Create a TT Template Page
395 First create a directory for book-related TT templates:
396
397 $ mkdir -p root/src/books
398
399 Then create "root/src/books/list.tt2" in your editor and enter:
400
401 [% # This is a TT comment. The '-' at the end "chomps" the newline. You won't -%]
402 [% # see this "chomping" in your browser because HTML ignores blank lines, but -%]
403 [% # it WILL eliminate a blank line if you view the HTML source. It's purely -%]
404 [%- # optional, but both the beginning and the ending TT tags support chomping. -%]
405
406 [% # Provide a title -%]
407 [% META title = 'Book List' -%]
408
409 <table>
410 <tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr>
411 [% # Display each book in a table row %]
412 [% FOREACH book IN books -%]
413 <tr>
414 <td>[% book.title %]</td>
415 <td>[% book.rating %]</td>
416 <td></td>
417 </tr>
418 [% END -%]
419 </table>
420
421 As indicated by the inline comments above, the "META title" line uses
422 TT's META feature to provide a title to the "wrapper" that we will
423 create later. Meanwhile, the "FOREACH" loop iterates through each
424 "book" model object and prints the "title" and "rating" fields.
425
426 The "[%" and "%]" tags are used to delimit Template Toolkit code. TT
427 supports a wide variety of directives for "calling" other files,
428 looping, conditional logic, etc. In general, TT simplifies the usual
429 range of Perl operators down to the single dot (".") operator. This
430 applies to operations as diverse as method calls, hash lookups, and
431 list index values (see
432 <http://search.cpan.org/perldoc?Template::Manual::Variables> for
433 details and examples). In addition to the usual Template module Pod
434 documentation, you can access the TT manual at
435 <http://search.cpan.org/perldoc?Template::Manual>.
436
437 TIP: While you can build all sorts of complex logic into your TT
438 templates, you should in general keep the "code" part of your templates
439 as simple as possible. If you need more complex logic, create helper
440 methods in your model that abstract out a set of code into a single
441 call from your TT template. (Note that the same is true of your
442 controller logic as well -- complex sections of code in your
443 controllers should often be pulled out and placed into your model
444 objects.) In Chapter 4 of the tutorial we will explore some extremely
445 helpful and powerful features of DBIx::Class that allow you to pull
446 code out of your views and controllers and place it where it rightfully
447 belongs in a model class.
448
449 Test Run The Application
450 To test your work so far, first start the development server:
451
452 $ script/myapp_server.pl -r
453
454 Then point your browser to <http://localhost:3000> and you should still
455 get the Catalyst welcome page. Next, change the URL in your browser to
456 <http://localhost:3000/books/list>. If you have everything working so
457 far, you should see a web page that displays nothing other than our
458 column headers for "Title", "Rating", and "Author(s)" -- we will not
459 see any books until we get the database and model working below.
460
461 If you run into problems getting your application to run correctly, it
462 might be helpful to refer to some of the debugging techniques covered
463 in the Debugging chapter of the tutorial.
464
466 In this step, we make a text file with the required SQL commands to
467 create a database table and load some sample data. We will use SQLite
468 (<http://www.sqlite.org>), a popular database that is lightweight and
469 easy to use. Be sure to get at least version 3. Open "myapp01.sql" in
470 your editor and enter:
471
472 --
473 -- Create a very simple database to hold book and author information
474 --
475 PRAGMA foreign_keys = ON;
476 CREATE TABLE book (
477 id INTEGER PRIMARY KEY,
478 title TEXT ,
479 rating INTEGER
480 );
481 -- 'book_author' is a many-to-many join table between books & authors
482 CREATE TABLE book_author (
483 book_id INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
484 author_id INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
485 PRIMARY KEY (book_id, author_id)
486 );
487 CREATE TABLE author (
488 id INTEGER PRIMARY KEY,
489 first_name TEXT,
490 last_name TEXT
491 );
492 ---
493 --- Load some sample data
494 ---
495 INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
496 INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
497 INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
498 INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
499 INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
500 INSERT INTO author VALUES (1, 'Greg', 'Bastien');
501 INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
502 INSERT INTO author VALUES (3, 'Christian', 'Degu');
503 INSERT INTO author VALUES (4, 'Richard', 'Stevens');
504 INSERT INTO author VALUES (5, 'Douglas', 'Comer');
505 INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
506 INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
507 INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
508 INSERT INTO book_author VALUES (1, 1);
509 INSERT INTO book_author VALUES (1, 2);
510 INSERT INTO book_author VALUES (1, 3);
511 INSERT INTO book_author VALUES (2, 4);
512 INSERT INTO book_author VALUES (3, 5);
513 INSERT INTO book_author VALUES (4, 6);
514 INSERT INTO book_author VALUES (4, 7);
515 INSERT INTO book_author VALUES (5, 8);
516
517 Then use the following command to build a "myapp.db" SQLite database:
518
519 $ sqlite3 myapp.db < myapp01.sql
520
521 If you need to create the database more than once, you probably want to
522 issue the "rm myapp.db" command to delete the database before you use
523 the "sqlite3 myapp.db < myapp01.sql" command.
524
525 Once the "myapp.db" database file has been created and initialized, you
526 can use the SQLite command line environment to do a quick dump of the
527 database contents:
528
529 $ sqlite3 myapp.db
530 SQLite version 3.6.22
531 Enter ".help" for instructions
532 Enter SQL statements terminated with a ";"
533 sqlite> select * from book;
534 1|CCSP SNRS Exam Certification Guide|5
535 2|TCP/IP Illustrated, Volume 1|5
536 3|Internetworking with TCP/IP Vol.1|4
537 4|Perl Cookbook|5
538 5|Designing with Web Standards|5
539 sqlite> .q
540 $
541
542 Or:
543
544 $ sqlite3 myapp.db "select * from book"
545 1|CCSP SNRS Exam Certification Guide|5
546 2|TCP/IP Illustrated, Volume 1|5
547 3|Internetworking with TCP/IP Vol.1|4
548 4|Perl Cookbook|5
549 5|Designing with Web Standards|5
550
551 As with most other SQL tools, if you are using the full "interactive"
552 environment you need to terminate your SQL commands with a ";" (it's
553 not required if you do a single SQL statement on the command line).
554 Use ".q" to exit from SQLite from the SQLite interactive mode and
555 return to your OS command prompt.
556
557 Please note that here we have chosen to use 'singular' table names.
558 This is because the default inflection code for older versions of
559 DBIx::Class::Schema::Loader does NOT handle plurals. There has been
560 much philosophical discussion on whether table names should be plural
561 or singular. There is no one correct answer, as long as one makes a
562 choice and remains consistent with it. If you prefer plural table names
563 (e.g. you think that they are easier to read) then see the
564 documentation in "naming" in DBIx::Class::Schema::Loader::Base (version
565 0.05 or greater).
566
567 For using other databases, such as PostgreSQL or MySQL, see Appendix 2.
568
570 Catalyst can be used with virtually any form of datastore available via
571 Perl. For example, Catalyst::Model::DBI can be used to access
572 databases through the traditional Perl DBI interface or you can use a
573 model to access files of any type on the filesystem. However, most
574 Catalyst applications use some form of object-relational mapping (ORM)
575 technology to create objects associated with tables in a relational
576 database. Matt Trout's DBIx::Class (abbreviated as "DBIC") has rapidly
577 emerged as the Perl-based ORM technology of choice. Most new Catalyst
578 applications rely on DBIx::Class, as will this tutorial.
579
580 Although DBIx::Class has included support for a "create=dynamic" mode
581 to automatically read the database structure every time the application
582 starts, it's use is no longer recommended. While it can make for
583 "flashy" demos, the use of the "create=static" mode we use below can be
584 implemented just as quickly and provides many advantages (such as the
585 ability to add your own methods to the overall DBIC framework, a
586 technique that we see in Chapter 4).
587
588 Make Sure You Have a Recent Version of the DBIx::Class Model
589 First, let's be sure we have a recent version of the DBIC helper,
590 Catalyst::Model::DBIC::Schema, so that we can take advantage of some
591 recent enhancements in how foreign keys are handled with SQLite. To
592 check your version, run this command:
593
594 $ perl -MCatalyst::Model::DBIC::Schema -e \
595 'print "$Catalyst::Model::DBIC::Schema::VERSION\n"'
596 0.4
597
598 Please note the '\' above. Depending on your environment, you might be
599 able to cut and paste the text as shown or need to remove the '\'
600 character to that the command is all on a single line.
601
602 If you are following along in Debian 5, you should have version 0.40 or
603 higher (shown above as "0.4" with the tailing zero removed). If you
604 have less than v0.39, you will need to run this command to install it
605 directly from CPAN:
606
607 $ sudo cpan Catalyst::Model::DBIC::Schema
608
609 And re-run the version print command to verify that you are now at 0.39
610 or higher.
611
612 In addition, since we are using SQLite's foreign key support here,
613 please be sure that you use version 1.27 of DBD::SQLite or later:
614
615 $ perl -MDBD::SQLite -e 'print "$DBD::SQLite::VERSION\n"'
616 1.29
617
618 Upgrade if you are not at version 1.27 or higher.
619
620 Create Static DBIx::Class Schema Files
621 Before you continue, make sure your "myapp.db" database file is in the
622 application's topmost directory. Now use the model helper with the
623 "create=static" option to read the database with
624 DBIx::Class::Schema::Loader and automatically build the required files
625 for us:
626
627 $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
628 create=static dbi:SQLite:myapp.db \
629 on_connect_do="PRAGMA foreign_keys = ON"
630 exists "/home/me/MyApp/script/../lib/MyApp/Model"
631 exists "/home/me/MyApp/script/../t"
632 Dumping manual schema for MyApp::Schema to directory /home/me/MyApp/script/../lib ...
633 Schema dump completed.
634 created "/home/me/MyApp/script/../lib/MyApp/Model/DB.pm"
635 created "/home/me/MyApp/script/../t/model_DB.t"
636
637 Please note the '\' above. Depending on your environment, you might be
638 able to cut and paste the text as shown or need to remove the '\'
639 character to that the command is all on a single line.
640
641 The "script/myapp_create.pl" command breaks down like this:
642
643 · "DB" is the name of the model class to be created by the helper in
644 "lib/MyApp/Model".
645
646 · "DBIC::Schema" is the type of the model to create.
647
648 · "MyApp::Schema" is the name of the DBIC schema file written to
649 "lib/MyApp/Schema.pm".
650
651 · "create=static" causes DBIx::Class::Schema::Loader to load the
652 schema as it runs and then write that information out into files.
653
654 · "dbi:SQLite:myapp.db" is the standard DBI connect string for use
655 with SQLite.
656
657 · And finally, the "on_connect_do" string requests that
658 DBIx::Class::Schema::Loader create foreign key relationships for us
659 (this is not needed for databases such as PostgreSQL and MySQL, but
660 is required for SQLite). If you take a look at
661 "lib/MyApp/Model/DB.pm", you will see that the SQLite pragma is
662 propogated to the Model, so that SQLite's recent (and optional)
663 foreign key enforcement is enabled at the start of every database
664 connection.
665
666 If you look in the "lib/MyApp/Schema.pm" file, you will find that it
667 only contains a call to the "load_namespaces" method. You will also
668 find that "lib/MyApp" contains a "Schema" subdirectory, which then has
669 a subdirectory called "Result". This "Result" subdirectory then has
670 files named according to each of the tables in our simple database
671 ("Author.pm", "BookAuthor.pm", and "Book.pm"). These three files are
672 called "Result Classes" in DBIx::Class nomenclature. Although the
673 Result Class files are named after tables in our database, the classes
674 correspond to the row-level data that is returned by DBIC (more on this
675 later, especially in "EXPLORING THE POWER OF DBIC" in
676 Catalyst::Manual::Tutorial::04_BasicCRUD).
677
678 The idea with the Result Source files created under
679 "lib/MyApp/Schema/Result" by the "create=static" option is to only edit
680 the files below the "# DO NOT MODIFY THIS OR ANYTHING ABOVE!" warning.
681 If you place all of your changes below that point in the file, you can
682 regenerate the automatically created information at the top of each
683 file should your database structure get updated.
684
685 Also note the "flow" of the model information across the various files
686 and directories. Catalyst will initially load the model from
687 "lib/MyApp/Model/DB.pm". This file contains a reference to
688 "lib/MyApp/Schema.pm", so that file is loaded next. Finally, the call
689 to "load_namespaces" in "Schema.pm" will load each of the "Result
690 Class" files from the "lib/MyApp/Schema/Result" subdirectory. The
691 final outcome is that Catalyst will dynamically create three table-
692 specific Catalyst models every time the application starts (you can see
693 these three model files listed in the debug output generated when you
694 launch the application).
695
696 NOTE: Older versions of Catalyst::Model::DBIC::Schema use the
697 deprecated DBIx::Class "load_classes" technique instead of the newer
698 "load_namspaces". For new applications, please try to use
699 "load_namespaces" since it more easily supports a very useful DBIC
700 technique called "ResultSet Classes." If you need to convert an
701 existing application from "load_classes" to "load_namespaces," you can
702 use this process to automate the migration, but first make sure you
703 have version 0.39 of Catalyst::Model::DBIC::Schema and
704 DBIx::Class::Schema::Loader version 0.05000 or later.
705
706 $ # Re-run the helper to upgrade for you
707 $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
708 create=static naming=current use_namespaces=1 \
709 dbi:SQLite:myapp.db \
710 on_connect_do="PRAGMA foreign_keys = ON"
711
713 Open "lib/MyApp/Controller/Books.pm" and un-comment the model code we
714 left disabled earlier so that your version matches the following (un-
715 comment the line containing "[$c->model('DB::Book')->all]" and delete
716 the next 2 lines):
717
718 =head2 list
719
720 Fetch all book objects and pass to books/list.tt2 in stash to be displayed
721
722 =cut
723
724 sub list :Local {
725 # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
726 # 'Context' that's used to 'glue together' the various components
727 # that make up the application
728 my ($self, $c) = @_;
729
730 # Retrieve all of the book records as book model objects and store
731 # in the stash where they can be accessed by the TT template
732 $c->stash(books => [$c->model('DB::Book')->all]);
733
734 # Set the TT template to use. You will almost always want to do this
735 # in your action methods (action methods respond to user input in
736 # your controllers).
737 $c->stash(template => 'books/list.tt2');
738 }
739
740 TIP: You may see the "$c->model('DB::Book')" un-commented above written
741 as "$c->model('DB')->resultset('Book')". The two are equivalent.
742 Either way, "$c->model" returns a DBIx::Class::ResultSet which handles
743 queries against the database and iterating over the set of results that
744 is returned.
745
746 We are using the "->all" to fetch all of the books. DBIC supports a
747 wide variety of more advanced operations to easily do things like
748 filtering and sorting the results. For example, the following could be
749 used to sort the results by descending title:
750
751 $c->model('DB::Book')->search({}, {order_by => 'title DESC'});
752
753 Some other examples are provided in "Complex WHERE clauses" in
754 DBIx::Class::Manual::Cookbook, with additional information found at
755 "search" in DBIx::Class::ResultSet, "Searching" in
756 DBIx::Class::Manual::FAQ, DBIx::Class::Manual::Intro and
757 Catalyst::Model::DBIC::Schema.
758
759 Test Run The Application
760 First, let's enable an environment variable that causes DBIx::Class to
761 dump the SQL statements used to access the database. This is a helpful
762 trick when you are trying to debug your database-oriented code. Press
763 "Ctrl-C" to break out of the development server and enter:
764
765 $ export DBIC_TRACE=1
766 $ script/myapp_server.pl -r
767
768 This assumes you are using bash as your shell -- adjust accordingly if
769 you are using a different shell (for example, under tcsh, use "setenv
770 DBIC_TRACE 1").
771
772 NOTE: You can also set this in your code using
773 "$class->storage->debug(1);". See DBIx::Class::Manual::Troubleshooting
774 for details (including options to log to a file instead of displaying
775 to the Catalyst development server log).
776
777 Then launch the Catalyst development server. The log output should
778 display something like:
779
780 $ script/myapp_server.pl -r
781 [debug] Debug messages enabled
782 [debug] Statistics enabled
783 [debug] Loaded plugins:
784 .----------------------------------------------------------------------------.
785 | Catalyst::Plugin::ConfigLoader 0.27 |
786 | Catalyst::Plugin::StackTrace 0.11 |
787 '----------------------------------------------------------------------------'
788
789 [debug] Loaded dispatcher "Catalyst::Dispatcher"
790 [debug] Loaded engine "Catalyst::Engine::HTTP"
791 [debug] Found home "/home/me/MyApp"
792 [debug] Loaded Config "/home/me/MyApp/myapp.conf"
793 [debug] Loaded components:
794 .-----------------------------------------------------------------+----------.
795 | Class | Type |
796 +-----------------------------------------------------------------+----------+
797 | MyApp::Controller::Books | instance |
798 | MyApp::Controller::Root | instance |
799 | MyApp::Model::DB | instance |
800 | MyApp::Model::DB::Author | class |
801 | MyApp::Model::DB::Book | class |
802 | MyApp::Model::DB::BookAuthor | class |
803 | MyApp::View::TT | instance |
804 '-----------------------------------------------------------------+----------'
805
806 [debug] Loaded Private actions:
807 .----------------------+--------------------------------------+--------------.
808 | Private | Class | Method |
809 +----------------------+--------------------------------------+--------------+
810 | /default | MyApp::Controller::Root | default |
811 | /end | MyApp::Controller::Root | end |
812 | /index | MyApp::Controller::Root | index |
813 | /books/index | MyApp::Controller::Books | index |
814 | /books/list | MyApp::Controller::Books | list |
815 '----------------------+--------------------------------------+--------------'
816
817 [debug] Loaded Path actions:
818 .-------------------------------------+--------------------------------------.
819 | Path | Private |
820 +-------------------------------------+--------------------------------------+
821 | / | /default |
822 | / | /index |
823 | /books | /books/index |
824 | /books/list | /books/list |
825 '-------------------------------------+--------------------------------------'
826
827 [info] MyApp powered by Catalyst 5.80020
828 You can connect to your server at http://debian:3000
829
830 NOTE: Be sure you run the "script/myapp_server.pl" command from the
831 'base' directory of your application, not inside the "script" directory
832 itself or it will not be able to locate the "myapp.db" database file.
833 You can use a fully qualified or a relative path to locate the database
834 file, but we did not specify that when we ran the model helper earlier.
835
836 Some things you should note in the output above:
837
838 · Catalyst::Model::DBIC::Schema dynamically created three model
839 classes, one to represent each of the three tables in our database
840 ("MyApp::Model::DB::Author", "MyApp::Model::DB::BookAuthor", and
841 "MyApp::Model::DB::Book").
842
843 · The "list" action in our Books controller showed up with a path of
844 "/books/list".
845
846 Point your browser to <http://localhost:3000> and you should still get
847 the Catalyst welcome page.
848
849 Next, to view the book list, change the URL in your browser to
850 <http://localhost:3000/books/list>. You should get a list of the five
851 books loaded by the "myapp01.sql" script above without any formatting.
852 The rating for each book should appear on each row, but the "Author(s)"
853 column will still be blank (we will fill that in later).
854
855 Also notice in the output of the "script/myapp_server.pl" that
856 DBIx::Class used the following SQL to retrieve the data:
857
858 SELECT me.id, me.title, me.rating FROM book me
859
860 because we enabled DBIC_TRACE.
861
862 You now have the beginnings of a simple but workable web application.
863 Continue on to future sections and we will develop the application more
864 fully.
865
867 When using TT, you can (and should) create a wrapper that will
868 literally wrap content around each of your templates. This is
869 certainly useful as you have one main source for changing things that
870 will appear across your entire site/application instead of having to
871 edit many individual files.
872
873 Configure TT.pm For The Wrapper
874 In order to create a wrapper, you must first edit your TT view and tell
875 it where to find your wrapper file.
876
877 Edit you TT view in "lib/MyApp/View/TT.pm" and change it to match the
878 following:
879
880 __PACKAGE__->config(
881 # Change default TT extension
882 TEMPLATE_EXTENSION => '.tt2',
883 # Set the location for TT files
884 INCLUDE_PATH => [
885 MyApp->path_to( 'root', 'src' ),
886 ],
887 # Set to 1 for detailed timer stats in your HTML as comments
888 TIMER => 0,
889 # This is your wrapper template located in the 'root/src'
890 WRAPPER => 'wrapper.tt2',
891 );
892
893 Create the Wrapper Template File and Stylesheet
894 Next you need to set up your wrapper template. Basically, you'll want
895 to take the overall layout of your site and put it into this file. For
896 the tutorial, open "root/src/wrapper.tt2" and input the following:
897
898 <?xml version="1.0" encoding="UTF-8"?>
899 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
900 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
901 <head>
902 <title>[% template.title or "My Catalyst App!" %]</title>
903 <link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
904 </head>
905
906 <body>
907 <div id="outer">
908 <div id="header">
909 [%# Your logo could go here -%]
910 <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
911 [%# Insert the page title -%]
912 <h1>[% template.title or site.title %]</h1>
913 </div>
914
915 <div id="bodyblock">
916 <div id="menu">
917 Navigation:
918 <ul>
919 <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
920 <li><a href="[% c.uri_for('/') %]" title="Catalyst Welcome Page">Welcome</a></li>
921 </ul>
922 </div><!-- end menu -->
923
924 <div id="content">
925 [%# Status and error messages %]
926 <span class="message">[% status_msg %]</span>
927 <span class="error">[% error_msg %]</span>
928 [%# This is where TT will stick all of your template's contents. -%]
929 [% content %]
930 </div><!-- end content -->
931 </div><!-- end bodyblock -->
932
933 <div id="footer">Copyright (c) your name goes here</div>
934 </div><!-- end outer -->
935
936 </body>
937 </html>
938
939 Notice the status and error message sections in the code above:
940
941 <span class="status">[% status_msg %]</span>
942 <span class="error">[% error_msg %]</span>
943
944 If we set either message in the Catalyst stash (e.g.,
945 "$c->stash->{status_msg} = 'Request was successful!'") it will be
946 displayed whenever any view used by that request is rendered. The
947 "message" and "error" CSS styles can be customized to suit your needs
948 in the "root/static/css/main.css" file we create below.
949
950 Notes:
951
952 · The Catalyst stash only lasts for a single HTTP request. If you
953 need to retain information across requests you can use
954 Catalyst::Plugin::Session (we will use Catalyst sessions in the
955 Authentication chapter of the tutorial).
956
957 · Although it is beyond the scope of this tutorial, you may wish to
958 use a JavaScript or AJAX tool such as jQuery
959 (<http://www.jquery.com>) or Dojo (<http://www.dojotoolkit.org>).
960
961 Create A Basic Stylesheet
962
963 First create a central location for stylesheets under the static
964 directory:
965
966 $ mkdir root/static/css
967
968 Then open the file "root/static/css/main.css" (the file referenced in
969 the stylesheet href link of our wrapper above) and add the following
970 content:
971
972 #header {
973 text-align: center;
974 }
975 #header h1 {
976 margin: 0;
977 }
978 #header img {
979 float: right;
980 }
981 #footer {
982 text-align: center;
983 font-style: italic;
984 padding-top: 20px;
985 }
986 #menu {
987 font-weight: bold;
988 background-color: #ddd;
989 }
990 #menu ul {
991 list-style: none;
992 float: left;
993 margin: 0;
994 padding: 0 0 50% 5px;
995 font-weight: normal;
996 background-color: #ddd;
997 width: 100px;
998 }
999 #content {
1000 margin-left: 120px;
1001 }
1002 .message {
1003 color: #390;
1004 }
1005 .error {
1006 color: #f00;
1007 }
1008
1009 You may wish to check out a "CSS Framework" like Emastic
1010 (<http://code.google.com/p/emastic/>) as a way to quickly provide lots
1011 of high-quality CSS functionality.
1012
1013 Test Run The Application
1014 Hit "Reload" in your web browser and you should now see a formatted
1015 version of our basic book list. (Again, the development server should
1016 have automatically restarted when you made changes to
1017 "lib/MyApp/View/TT.pm". If you are not using the "-r" option, you will
1018 need to hit "Ctrl-C" and manually restart it. Also note that the
1019 development server does NOT need to restart for changes to the TT and
1020 static files we created and edited in the "root" directory -- those
1021 updates are handled on a per-request basis.)
1022
1023 Although our wrapper and stylesheet are obviously very simple, you
1024 should see how it allows us to control the overall look of an entire
1025 website from two central files. To add new pages to the site, just
1026 provide a template that fills in the "content" section of our wrapper
1027 template -- the wrapper will provide the overall feel of the page.
1028
1029 Updating the Generated DBIx::Class Result Class Files
1030 If you take a look at the Schema files automatically generated by
1031 DBIx::Class::Schema::Loader, you will see that it has already defined
1032 "has_many" and "belongs_to" relationships on each side of our foreign
1033 keys. For example, take a look at "lib/MyApp/Schema/Result/Book.pm" and
1034 notice the following code:
1035
1036 =head1 RELATIONS
1037
1038 =head2 book_authors
1039
1040 Type: has_many
1041
1042 Related object: L<MyApp::Schema::Result::BookAuthor>
1043
1044 =cut
1045
1046 __PACKAGE__->has_many(
1047 "book_authors",
1048 "MyApp::Schema::Result::BookAuthor",
1049 { "foreign.book_id" => "self.id" },
1050 );
1051
1052 Each "Book" "has_many" "book_authors", where "BookAuthor" is the many-
1053 to-many table that allows each Book to have multiple Authors, and each
1054 Author to have mulitple books. The arguments to "has_many" are:
1055
1056 · "book_authors" - The name for this relationship. DBIC will create
1057 an accessor on the "Books" DBIC Row object with this name.
1058
1059 · "MyApp::Schema::Result::BookAuthor" - The name of the DBIC model
1060 class referenced by this "has_many" relationship.
1061
1062 · "foreign.book_id" - "book_id" is the name of the foreign key column
1063 in the foreign table that points back to this table.
1064
1065 · "self.id" - "id" is the name of the column in this table that is
1066 referenced by the foreign key.
1067
1068 See "has_many" in DBIx::Class::Relationship for additional information.
1069 Note that you might see a "hand coded" version of the "has_many"
1070 relationship above expressed as:
1071
1072 __PACKAGE__->has_many(
1073 "book_authors",
1074 "MyApp::Schema::Result::BookAuthor",
1075 "book_id",
1076 );
1077
1078 Where the third argument is simply the name of the column in the
1079 foreign table. However, the hashref syntax used by
1080 DBIx::Class::Schema::Loader is more flexible (for example, it can
1081 handle "multi-column" foreign keys).
1082
1083 Note: If you are using older versions of SQLite and related DBIC tools,
1084 you will need to manually define your "has_many" and "belongs_to"
1085 relationships. We recommend upgrading to the versions specified above.
1086 :-)
1087
1088 Have a look at "lib/MyApp/Schema/Result/BookAuthor.pm" and notice that
1089 there is a "belongs_to" relationship defined that acts as the "mirror
1090 image" to the "has_many" relationship we just looked at above:
1091
1092 =head1 RELATIONS
1093
1094 =head2 book
1095
1096 Type: belongs_to
1097
1098 Related object: L<MyApp::Schema::Result::Book>
1099
1100 =cut
1101
1102 __PACKAGE__->belongs_to(
1103 "book",
1104 "MyApp::Schema::Result::Book",
1105 { id => "book_id" },
1106 { join_type => "LEFT" },
1107 );
1108
1109 The arguments are similar, but see "belongs_to" in
1110 DBIx::Class::Relationship for the details.
1111
1112 Although recent versions of SQLite and DBIx::Class::Schema::Loader
1113 automatically handle the "has_many" and "belongs_to" relationships,
1114 "many_to_many" relationships currently need to be manually inserted.
1115 To add a "many_to_many" relationship, first edit
1116 "lib/MyApp/Schema/Result/Book.pm" and add the following text below the
1117 "# You can replace this text..." comment:
1118
1119 # many_to_many():
1120 # args:
1121 # 1) Name of relationship, DBIC will create accessor with this name
1122 # 2) Name of has_many() relationship this many_to_many() is shortcut for
1123 # 3) Name of belongs_to() relationship in model class of has_many() above
1124 # You must already have the has_many() defined to use a many_to_many().
1125 __PACKAGE__->many_to_many(authors => 'book_authors', 'author');
1126
1127 Note: Be careful to put this code above the "1;" at the end of the
1128 file. As with any Perl package, we need to end the last line with a
1129 statement that evaluates to "true". This is customarily done with "1;"
1130 on a line by itself.
1131
1132 The "many_to_many" relationship is optional, but it makes it easier to
1133 map a book to its collection of authors. Without it, we would have to
1134 "walk" though the "book_author" table as in
1135 "$book->book_author->first->author->last_name" (we will see examples on
1136 how to use DBIx::Class objects in your code soon, but note that because
1137 "$book->book_author" can return multiple authors, we have to use
1138 "first" to display a single author). "many_to_many" allows us to use
1139 the shorter "$book->author->first->last_name". Note that you cannot
1140 define a "many_to_many" relationship without also having the "has_many"
1141 relationship in place.
1142
1143 Then edit "lib/MyApp/Schema/Result/Author.pm" and add the reverse
1144 "many_to_many" relationship for "Author" as follows (again, be careful
1145 to put in above the "1;" but below the "# DO NOT MODIFY THIS OR
1146 ANYTHING ABOVE!" comment):
1147
1148 # many_to_many():
1149 # args:
1150 # 1) Name of relationship, DBIC will create accessor with this name
1151 # 2) Name of has_many() relationship this many_to_many() is shortcut for
1152 # 3) Name of belongs_to() relationship in model class of has_many() above
1153 # You must already have the has_many() defined to use a many_to_many().
1154 __PACKAGE__->many_to_many(books => 'book_authors', 'book');
1155
1156 Run The Application
1157 Run the Catalyst development server script with the "DBIC_TRACE" option
1158 (it might still be enabled from earlier in the tutorial, but here is an
1159 alternate way to specify the trace option just in case):
1160
1161 $ DBIC_TRACE=1 script/myapp_server.pl -r
1162
1163 Make sure that the application loads correctly and that you see the
1164 three dynamically created model class (one for each of the Result
1165 Classes we created).
1166
1167 Then hit the URL <http://localhost:3000/books/list> with your browser
1168 and be sure that the book list still displays correctly.
1169
1170 Note: You will not see the authors yet because the view does not yet
1171 use the new relations. Read on to the next section where we update the
1172 template to do that.
1173
1175 Let's add a new column to our book list page that takes advantage of
1176 the relationship information we manually added to our schema files in
1177 the previous section. Edit "root/src/books/list.tt2" and replace the
1178 "empty" table cell "<td></td>" with the following:
1179
1180 ...
1181 <td>
1182 [% # NOTE: See Chapter 4 for a better way to do this! -%]
1183 [% # First initialize a TT variable to hold a list. Then use a TT FOREACH -%]
1184 [% # loop in 'side effect notation' to load just the last names of the -%]
1185 [% # authors into the list. Note that the 'push' TT vmethod doesn't return -%]
1186 [% # a value, so nothing will be printed here. But, if you have something -%]
1187 [% # in TT that does return a value and you don't want it printed, you -%]
1188 [% # 1) assign it to a bogus value, or -%]
1189 [% # 2) use the CALL keyword to call it and discard the return value. -%]
1190 [% tt_authors = [ ];
1191 tt_authors.push(author.last_name) FOREACH author = book.authors %]
1192 [% # Now use a TT 'virtual method' to display the author count in parens -%]
1193 [% # Note the use of the TT filter "| html" to escape dangerous characters -%]
1194 ([% tt_authors.size | html %])
1195 [% # Use another TT vmethod to join & print the names & comma separators -%]
1196 [% tt_authors.join(', ') | html %]
1197 </td>
1198 ...
1199
1200 IMPORTANT NOTE: Again, you should keep as much "logic code" as possible
1201 out of your views. This kind of logic belongs in your model (the same
1202 goes for controllers -- keep them as "thin" as possible and push all of
1203 the "complicated code" out to your model objects). Avoid code like you
1204 see in the previous example -- we are only using it here to show some
1205 extra features in TT until we get to the more advanced model features
1206 we will see in Chapter 4 (see "EXPLORING THE POWER OF DBIC" in
1207 Catalyst::Manual::Tutorial::04_BasicCRUD).
1208
1209 Then hit "Reload" in your browser (note that you don't need to reload
1210 the development server or use the "-r" option when updating TT
1211 templates) and you should now see the number of authors each book has
1212 along with a comma-separated list of the authors' last names. (If you
1213 didn't leave the development server running from the previous step, you
1214 will obviously need to start it before you can refresh your browser
1215 window.)
1216
1217 If you are still running the development server with "DBIC_TRACE"
1218 enabled, you should also now see five more "SELECT" statements in the
1219 debug output (one for each book as the authors are being retrieved by
1220 DBIx::Class):
1221
1222 SELECT me.id, me.title, me.rating FROM book me:
1223 SELECT author.id, author.first_name, author.last_name FROM book_author me
1224 JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '1'
1225 SELECT author.id, author.first_name, author.last_name FROM book_author me
1226 JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '2'
1227 SELECT author.id, author.first_name, author.last_name FROM book_author me
1228 JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '3'
1229 SELECT author.id, author.first_name, author.last_name FROM book_author me
1230 JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '4'
1231 SELECT author.id, author.first_name, author.last_name FROM book_author me
1232 JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '5'
1233
1234 Also note in "root/src/books/list.tt2" that we are using "| html", a
1235 type of TT filter, to escape characters such as < and > to < and
1236 > and avoid various types of dangerous hacks against your
1237 application. In a real application, you would probably want to put "|
1238 html" at the end of every field where a user has control over the
1239 information that can appear in that field (and can therefore inject
1240 markup or code if you don't "neutralize" those fields). In addition to
1241 "| html", Template Toolkit has a variety of other useful filters that
1242 can found in the documentation for Template::Filters.
1243
1245 In some situations, it can be useful to run your application and
1246 display a page without using a browser. Catalyst lets you do this
1247 using the "scripts/myapp_test.pl" script. Just supply the URL you wish
1248 to display and it will run that request through the normal controller
1249 dispatch logic and use the appropriate view to render the output
1250 (obviously, complex pages may dump a lot of text to your terminal
1251 window). For example, if you type:
1252
1253 $ script/myapp_test.pl "/books/list"
1254
1255 You should get the same text as if you visited
1256 <http://localhost:3000/books/list> with the normal development server
1257 and asked your browser to view the page source.
1258
1260 NOTE: The rest of this chapter of the tutorial is optional. You can
1261 skip to Chapter 4, Basic CRUD, if you wish.
1262
1263 Using 'RenderView' for the Default View
1264 Once your controller logic has processed the request from a user, it
1265 forwards processing to your view in order to generate the appropriate
1266 response output. Catalyst uses Catalyst::Action::RenderView by default
1267 to automatically perform this operation. If you look in
1268 "lib/MyApp/Controller/Root.pm", you should see the empty definition for
1269 the "sub end" method:
1270
1271 sub end : ActionClass('RenderView') {}
1272
1273 The following bullet points provide a quick overview of the
1274 "RenderView" process:
1275
1276 · "Root.pm" is designed to hold application-wide logic.
1277
1278 · At the end of a given user request, Catalyst will call the most
1279 specific "end" method that's appropriate. For example, if the
1280 controller for a request has an "end" method defined, it will be
1281 called. However, if the controller does not define a controller-
1282 specific "end" method, the "global" "end" method in "Root.pm" will
1283 be called.
1284
1285 · Because the definition includes an "ActionClass" attribute, the
1286 Catalyst::Action::RenderView logic will be executed after any code
1287 inside the definition of "sub end" is run. See
1288 Catalyst::Manual::Actions for more information on "ActionClass".
1289
1290 · Because "sub end" is empty, this effectively just runs the default
1291 logic in "RenderView". However, you can easily extend the
1292 "RenderView" logic by adding your own code inside the empty method
1293 body ("{}") created by the Catalyst Helpers when we first ran the
1294 "catalyst.pl" to initialize our application. See
1295 Catalyst::Action::RenderView for more detailed information on how
1296 to extend "RenderView" in "sub end".
1297
1298 RenderView's "dump_info" Feature
1299 One of the nice features of "RenderView" is that it automatically
1300 allows you to add "dump_info=1" to the end of any URL for your
1301 application and it will force the display of the "exception dump"
1302 screen to the client browser. You can try this out by pointing your
1303 browser to this URL:
1304
1305 http://localhost:3000/books/list?dump_info=1
1306
1307 You should get a page with the following message at the top:
1308
1309 Caught exception in MyApp::Controller::Root->end "Forced debug -
1310 Scrubbed output at /usr/share/perl5/Catalyst/Action/RenderView.pm line 46."
1311
1312 Along with a summary of your application's state at the end of the
1313 processing for that request. The "Stash" section should show a
1314 summarized version of the DBIC book model objects. If desired, you can
1315 adjust the summarization logic (called "scrubbing" logic) -- see
1316 Catalyst::Action::RenderView for details.
1317
1318 Note that you shouldn't need to worry about "normal clients" using this
1319 technique to "reverse engineer" your application -- "RenderView" only
1320 supports the "dump_info=1" feature when your application is running in
1321 "-Debug" mode (something you won't do once you have your application
1322 deployed in production).
1323
1324 Using The Default Template Name
1325 By default, "Catalyst::View::TT" will look for a template that uses the
1326 same name as your controller action, allowing you to save the step of
1327 manually specifying the template name in each action. For example,
1328 this would allow us to remove the "$c->stash->{template} =
1329 'books/list.tt2';" line of our "list" action in the Books controller.
1330 Open "lib/MyApp/Controller/Books.pm" in your editor and comment out
1331 this line to match the following (only the "$c->stash->{template}" line
1332 has changed):
1333
1334 =head2 list
1335
1336 Fetch all book objects and pass to books/list.tt2 in stash to be displayed
1337
1338 =cut
1339
1340 sub list :Local {
1341 # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
1342 # 'Context' that's used to 'glue together' the various components
1343 # that make up the application
1344 my ($self, $c) = @_;
1345
1346 # Retrieve all of the book records as book model objects and store in the
1347 # stash where they can be accessed by the TT template
1348 $c->stash(books => [$c->model('DB::Book')->all]);
1349
1350 # Set the TT template to use. You will almost always want to do this
1351 # in your action methods (actions methods respond to user input in
1352 # your controllers).
1353 #$c->stash(template => 'books/list.tt2');
1354 }
1355
1356 You should now be able to access the <http://localhost:3000/books/list>
1357 URL as before.
1358
1359 NOTE: Please note that if you use the default template technique, you
1360 will not be able to use either the "$c->forward" or the "$c->detach"
1361 mechanisms (these are discussed in Chapter 2 and Chapter 9 of the
1362 Tutorial).
1363
1364 IMPORTANT: Make sure that you do NOT skip the following section before
1365 continuing to the next chapter 4 Basic CRUD.
1366
1367 Return To A Manually Specified Template
1368 In order to be able to use "$c->forward" and "$c->detach" later in the
1369 tutorial, you should remove the comment from the statement in "sub
1370 list" in "lib/MyApp/Controller/Books.pm":
1371
1372 $c->stash(template => 'books/list.tt2');
1373
1374 Then delete the "TEMPLATE_EXTENSION" line in "lib/MyApp/View/TT.pm".
1375
1376 Check the <http://localhost:3000/books/list> URL in your browser. It
1377 should look the same manner as with earlier sections.
1378
1380 Kennedy Clark, "hkclark@gmail.com"
1381
1382 Please report any errors, issues or suggestions to the author. The
1383 most recent version of the Catalyst Tutorial can be found at
1384 http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/
1385 <http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-
1386 Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
1387
1388 Copyright 2006-2008, Kennedy Clark, under Creative Commons License
1389 (http://creativecommons.org/licenses/by-sa/3.0/us/
1390 <http://creativecommons.org/licenses/by-sa/3.0/us/>).
1391
1392
1393
1394perl v5.12.0 Catalyst2:0:1M0a-n0u2a-l1:7:Tutorial::03_MoreCatalystBasics(3)