Commit | Line | Data |
3533daff |
1 | =head1 NAME |
2 | |
3 | Catalyst::Manual::Tutorial::MoreCatalystBasics - Catalyst Tutorial - Part 3: More Catalyst Application Development Basics |
4 | |
5 | |
6 | =head1 OVERVIEW |
7 | |
8 | This is B<Part 3 of 10> for the Catalyst tutorial. |
9 | |
10 | L<Tutorial Overview|Catalyst::Manual::Tutorial> |
11 | |
12 | =over 4 |
13 | |
14 | =item 1 |
15 | |
16 | L<Introduction|Catalyst::Manual::Tutorial::Intro> |
17 | |
18 | =item 2 |
19 | |
20 | L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics> |
21 | |
22 | =item 3 |
23 | |
24 | B<More Catalyst Basics> |
25 | |
26 | =item 4 |
27 | |
28 | L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD> |
29 | |
30 | =item 5 |
31 | |
32 | L<Authentication|Catalyst::Manual::Tutorial::Authentication> |
33 | |
34 | =item 6 |
35 | |
36 | L<Authorization|Catalyst::Manual::Tutorial::Authorization> |
37 | |
38 | =item 7 |
39 | |
40 | L<Debugging|Catalyst::Manual::Tutorial::Debugging> |
41 | |
42 | =item 8 |
43 | |
44 | L<Testing|Catalyst::Manual::Tutorial::Testing> |
45 | |
46 | =item 9 |
47 | |
48 | L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD> |
49 | |
50 | =item 10 |
51 | |
52 | L<Appendices|Catalyst::Manual::Tutorial::Appendices> |
53 | |
54 | =back |
55 | |
56 | |
57 | =head1 DESCRIPTION |
58 | |
59 | This part of the tutorial builds on the work done in Part 2 to explore |
60 | some features that are more typical of "real world" web applications. |
61 | From this part of the tutorial onward, we will be building a simple |
62 | book database application. Although the application will be too |
63 | limited to be of use to anyone, it should provide a basic environment |
64 | where we can explore a variety of features used in virtually all web |
65 | applications. |
66 | |
67 | You can checkout the source code for this example from the catalyst |
68 | subversion repository as per the instructions in |
69 | L<Catalyst::Manual::Tutorial::Intro> |
70 | |
71 | |
72 | =head1 CREATE A NEW APPLICATION |
73 | |
74 | The remainder of the tutorial will build an application call C<MyApp>. |
75 | Use the Catalyst C<catalyst.pl> script to initialize the framework for |
76 | an application called C<MyApp> (make sure you aren't still inside the |
77 | directory of the C<Hello> application from the previous part of the |
78 | tutorial): |
79 | |
80 | $ catalyst.pl MyApp |
81 | created "MyApp" |
82 | created "MyApp/script" |
83 | created "MyApp/lib" |
84 | created "MyApp/root" |
85 | ... |
86 | created "MyApp/script/myapp_create.pl" |
87 | $ cd MyApp |
88 | |
89 | This creates a similar skeletal structure to what we saw in Part 2 of |
90 | the tutorial, except with C<MyApp> or C<myapp> substituted for |
91 | C<Hello> and C<hello>. |
92 | |
93 | |
94 | =head1 EDIT THE LIST OF CATALYST PLUGINS |
95 | |
96 | One of the greatest benefits of Catalyst is that it has such a large |
97 | library of plugins available. Plugins are used to seamlessly integrate |
98 | existing Perl modules into the overall Catalyst framework. In general, |
99 | they do this by adding additional methods to the C<context> object |
100 | (generally written as C<$c>) that Catalyst passes to every component |
101 | throughout the framework. |
102 | |
103 | By default, Catalyst enables three plugins/flags: |
104 | |
105 | =over 4 |
106 | |
107 | =item * |
108 | |
109 | C<-Debug> Flag |
110 | |
111 | Enables the Catalyst debug output you saw when we started the |
112 | C<script/myapp_server.pl> development server earlier. You can remove |
113 | this plugin when you place your application into production. |
114 | |
115 | As you may have noticed, C<-Debug> is not a plugin, but a I<flag>. |
116 | Although most of the items specified on the C<use Catalyst> line of your |
117 | application class will be plugins, Catalyst supports a limited number of |
118 | flag options (of these, C<-Debug> is the most common). See the |
119 | documentation for C<Catalyst.pm> to get details on other flags |
120 | (currently C<-Engine>, C<-Home>, and C<-Log>). |
121 | |
122 | If you prefer, you can use the C<$c-E<gt>debug> method to enable debug |
123 | messages. |
124 | |
125 | B<TIP>: Depending on your needs, it can be helpful to permanently |
126 | remove C<-Debug> from C<lib/MyApp.pm> and then use the C<-d> option |
127 | to C<script/myapp_server.pl> to re-enable it just for the development |
128 | server. We will not be using that approach in the tutorial, but feel |
129 | free to make use of it in your own projects. |
130 | |
131 | =item * |
132 | |
133 | L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> |
134 | |
135 | C<ConfigLoader> provides an automatic way to load configurable |
c010ae0d |
136 | parameters for your application from a central |
137 | L<Config::General|Config::General> file (versus having the values |
138 | hard-coded inside your Perl modules). Config::General uses syntax |
139 | very similar to Apache configuration files. We will see how to use |
140 | this feature of Catalyst during the authentication and authorization |
141 | sections (Part 5 and Part 6). |
3533daff |
142 | |
15e1d0b2 |
143 | B<IMPORTANT NOTE>: If you are following along in Ubuntu 8.04 or |
144 | otherwise using a version of Catalyst prior to v5.7014, you need to be |
145 | aware that Catalyst changed from a default format of YAML to the more |
146 | straightforward C<Config::General> format. Because Catalyst has long |
147 | supported both formats, this tutorial will simply use a configuration |
148 | file called C<myapp.conf> instead of C<myapp.yml> and Catatlyst will |
149 | automcatically use the new format. Just be aware that earlier versions |
150 | of Catalyst will still create the C<myapp.yml> file and that you will |
151 | need to B<remove C<myapp.yml>> and create a new C<myapp.conf> file by |
152 | hand, but otherwise this transition is very painless. The default |
153 | contents of C<myapp.conf> should only consist of one line: C<name |
154 | MyApp>. Also be aware that you can continue to use any format |
155 | supported by |
156 | L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> and |
157 | L<Config::Any|Config::Any>, including YAML -- Catalyst will |
158 | automatically look for any of the supported configuration file formats. |
159 | |
160 | C<TIP>: This script can be useful for converting between configuration |
161 | formats: |
162 | |
163 | perl -Ilib -e 'use MyApp; use Config::General; |
164 | Config::General->new->save_file("myapp.conf", MyApp->config);' |
165 | |
166 | |
3533daff |
167 | =item * |
168 | |
169 | L<Catalyst::Plugin::Static::Simple|Catalyst::Plugin::Static::Simple> |
170 | |
171 | C<Static::Simple> provides an easy method of serving static content such |
172 | as images and CSS files under the development server. |
173 | |
174 | =back |
175 | |
176 | To modify the list of plugins, edit C<lib/MyApp.pm> (this file is |
177 | generally referred to as your I<application class>) and delete the line |
178 | with: |
179 | |
180 | use Catalyst qw/-Debug ConfigLoader Static::Simple/; |
181 | |
182 | Replace it with: |
183 | |
184 | use Catalyst qw/ |
185 | -Debug |
186 | ConfigLoader |
187 | Static::Simple |
188 | |
189 | StackTrace |
190 | /; |
191 | |
192 | This tells Catalyst to start using one new plugin: |
193 | |
194 | =over 4 |
195 | |
196 | =item * |
197 | |
198 | L<Catalyst::Plugin::StackTrace|Catalyst::Plugin::StackTrace> |
199 | |
200 | Adds a stack trace to the standard Catalyst "debug screen" (this is the |
201 | screen Catalyst sends to your browser when an error occurs). |
202 | |
203 | Note: L<StackTrace|Catalyst::Plugin::StackTrace> output appears in your |
204 | browser, not in the console window from which you're running your |
205 | application, which is where logging output usually goes. |
206 | |
207 | =back |
208 | |
209 | Note that when specifying plugins on the C<use Catalyst> line, you can |
210 | omit C<Catalyst::Plugin::> from the name. Additionally, you can spread |
211 | the plugin names across multiple lines as shown here, or place them all |
212 | on one (or more) lines as with the default configuration. |
213 | |
214 | |
215 | =head1 CREATE A CATALYST CONTROLLER |
216 | |
217 | As discussed earlier, controllers are where you write methods that |
218 | interact with user input. Typically, controller methods respond to |
219 | C<GET> and C<POST> messages from the user's web browser. |
220 | |
221 | Use the Catalyst C<create> script to add a controller for book-related |
222 | actions: |
223 | |
224 | $ script/myapp_create.pl controller Books |
225 | exists "/home/me/MyApp/script/../lib/MyApp/Controller" |
226 | exists "/home/me/MyApp/script/../t" |
227 | created "/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm" |
228 | created "/home/me/MyApp/script/../t/controller_Books.t" |
229 | |
230 | Then edit C<lib/MyApp/Controller/Books.pm> and add the following method |
231 | to the controller: |
232 | |
233 | =head2 list |
234 | |
235 | Fetch all book objects and pass to books/list.tt2 in stash to be displayed |
236 | |
237 | =cut |
238 | |
239 | sub list : Local { |
240 | # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst |
241 | # 'Context' that's used to 'glue together' the various components |
242 | # that make up the application |
243 | my ($self, $c) = @_; |
244 | |
245 | # Retrieve all of the book records as book model objects and store in the |
246 | # stash where they can be accessed by the TT template |
247 | $c->stash->{books} = [$c->model('MyAppDB::Books')->all]; |
248 | |
249 | # Set the TT template to use. You will almost always want to do this |
250 | # in your action methods (action methods respond to user input in |
251 | # your controllers). |
252 | $c->stash->{template} = 'books/list.tt2'; |
253 | } |
254 | |
255 | B<Note:> This won't actually work yet since you haven't set up your |
256 | model yet. |
257 | |
258 | B<Note:> Programmers experienced with object-oriented Perl should |
259 | recognize C<$self> as a reference to the object where this method was |
260 | called. On the other hand, C<$c> will be new to many Perl programmers |
261 | who have not used Catalyst before (it's sometimes written as |
262 | C<$context>). The Context object is automatically passed to all |
263 | Catalyst components. It is used to pass information between |
264 | components and provide access to Catalyst and plugin functionality. |
265 | |
266 | B<TIP>: You may see the C<$c-E<gt>model('MyAppDB::Book')> used above |
267 | written as C<$c-E<gt>model('MyAppDB')-E<gt>resultset('Book)>. The two |
268 | are equivalent. |
269 | |
270 | B<Note:> Catalyst actions are regular Perl methods, but they make use |
271 | of Nicholas Clark's C<attributes> module (that's the C<: Local> next |
272 | to the C<sub list> in the code above) to provide additional |
273 | information to the Catalyst dispatcher logic. Many newer Catalyst |
274 | applications are switching to the use of "Literal" C<: Path> actions |
275 | and C<Args> attribute in lieu of C<: Local> and C<: Private>. For |
276 | example, C<sub any_method : Path Args(0)> can be used instead of |
277 | C<sub index :Private> (because no path was supplied to C<Path> it |
278 | matches the "empty" URL in the namespace of that module... the same |
279 | thing C<sub index> would do) or C<sub list : Path('list') Args(0)> |
280 | could be used instead of the C<sub list : Local> above (the C<list> |
281 | argument to C<Path> would make it match on the URL C<list> under |
282 | C<books>, the namespace of the current module). See "Action Types" in |
283 | L<Catalyst::Manual::Intro|Catalyst::Manual::Intro> as well as Part 5 |
284 | of this tutorial (Authentication) for additional information. Another |
285 | popular but more advanced feature is C<Chained> actions that allow a |
286 | single URL to "chain together" multiple action method calls, each with |
287 | an appropriate number of arguments (see |
288 | L<Catalyst::DispatchType::Chained|Catalyst::DispatchType::Chained> |
289 | for details). |
290 | |
291 | |
292 | =head1 CATALYST VIEWS |
293 | |
294 | As mentioned in Part 2 of the tutorial, views are where you render |
295 | output, typically for display in the user's web browser, but also |
296 | possibly using other display output- generation systems. As with |
297 | virtually every aspect of Catalyst, options abound when it comes to |
298 | the specific view technology you adopt inside your application. |
299 | However, most Catalyst applications use the Template Toolkit, known as |
300 | TT (for more information on TT, see L<http://www.template- |
301 | toolkit.org>). Other popular view technologies include Mason |
302 | (L<http://www.masonhq.com> and L<http://www.masonbook.com>) and |
303 | L<HTML::Template|HTML::Template> (L<http://html- |
304 | template.sourceforge.net>). |
305 | |
306 | =head2 Create a Catalyst View Using C<TTSite> |
307 | |
308 | When using TT for the Catalyst view, there are two main helper scripts: |
309 | |
310 | =over 4 |
311 | |
312 | =item * |
313 | |
314 | L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT> |
315 | |
316 | =item * |
317 | |
318 | L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite> |
319 | |
320 | =back |
321 | |
322 | Both are similar, but C<TT> merely creates the C<lib/MyApp/View/TT.pm> |
323 | file and leaves the creation of any hierarchical template organization |
324 | entirely up to you. (It also creates a C<t/view_TT.t> file for testing; |
325 | test cases will be discussed in Part 8). The C<TTSite> helper creates a |
326 | modular and hierarchical view layout with separate Template Toolkit (TT) |
327 | files for common header and footer information, configuration values, a |
328 | CSS stylesheet, and more. |
329 | |
330 | While TTSite is useful to bootstrap a project, we recommend that |
331 | unless you know what you're doing or want to pretty much use the |
332 | supplied templates as is, that you use the plain Template Toolkit view |
333 | when starting a project from scratch. This is because TTSite can be |
334 | tricky to customize. Additionally TT contains constructs that you |
335 | need to learn yourself if you're going to be a serious user of TT. |
336 | Our experience suggests that you're better off learning these from |
337 | scratch. We use TTSite here precisely because it is useful for |
338 | bootstrap/prototype purposes. |
339 | |
340 | Enter the following command to enable the C<TTSite> style of view |
341 | rendering for this tutorial: |
342 | |
343 | $ script/myapp_create.pl view TT TTSite |
344 | exists "/home/me/MyApp/script/../lib/MyApp/View" |
345 | exists "/home/me/MyApp/script/../t" |
346 | created "/home/me/MyApp/script/../lib/MyApp/View/TT.pm" |
347 | created "/home/me/MyApp/script/../root/lib" |
348 | ... |
349 | created "/home/me/MyApp/script/../root/src/ttsite.css" |
350 | |
351 | This puts a number of files in the C<root/lib> and C<root/src> |
352 | directories that can be used to customize the look and feel of your |
353 | application. Also take a look at C<lib/MyApp/View/TT.pm> for config |
354 | values set by the C<TTSite> helper. |
355 | |
356 | B<TIP>: Note that TTSite does one thing that could confuse people who |
357 | are used to the normal C<TT> Catalyst view: it redefines the Catalyst |
358 | context object in templates from its usual C<c> to C<Catalyst>. When |
359 | looking at other Catalyst examples, remember that they almost always use |
360 | C<c>. Note that Catalyst and TT I<do not complain> when you use the |
361 | wrong name to access the context object...TT simply outputs blanks for |
362 | that bogus logic (see next tip to change this behavior with TT C<DEBUG> |
363 | options). Finally, be aware that this change in name I<only> |
364 | applies to how the context object is accessed inside your TT templates; |
365 | your controllers will continue to use C<$c> (or whatever name you use |
366 | when fetching the reference from C<@_> inside your methods). (You can |
367 | change back to the "default" behavior be removing the C<CATALYST_VAR> |
368 | line from C<lib/MyApp/View/TT.pm>, but you will also have to edit |
369 | C<root/lib/config/main> and C<root/lib/config/url>. If you do this, be |
370 | careful not to have a collision between your own C<c> variable and the |
371 | Catalyst C<c> variable.) |
372 | |
373 | B<TIP>: When troubleshooting TT it can be helpful to enable variable |
374 | C<DEBUG> options. You can do this in a Catalyst environment by adding |
375 | a C<DEBUG> line to the C<__PACKAGE__->config> declaration in |
376 | C<lib/MyApp/View/TT.pm>: |
377 | |
378 | __PACKAGE__->config({ |
379 | CATALYST_VAR => 'Catalyst', |
380 | ... |
381 | DEBUG => 'undef', |
382 | ... |
383 | }); |
384 | |
385 | B<Note:> C<__PACKAGE__> is just a shorthand way of referencing the name |
386 | of the package where it is used. Therefore, in C<TT.pm>, |
387 | C<__PACKAGE__> is equivalent to C<TT>. |
388 | |
389 | There are a variety of options you can use, such as 'undef', 'all', |
390 | 'service', 'context', 'parser', 'provider', and 'service'. See |
391 | L<Template::Constants> for more information (remove the C<DEBUG_> |
392 | portion of the name shown in the TT docs and convert to lower case |
393 | for use inside Catalyst). |
394 | |
395 | B<NOTE:> B<Please be sure to disable TT debug options before |
396 | continuing the tutorial> (especially the 'undef' option -- leaving |
397 | this enabled will conflict with several of the conventions used |
398 | by this tutorial and TTSite to leave some variables undefined |
399 | on purpose). |
400 | |
401 | |
402 | =head2 Globally Customize Every View |
403 | |
404 | When using TTSite, files in the subdirectories of C<root/lib> can be |
405 | used to make changes that will appear in every view. For example, to |
406 | display optional status and error messages in every view, edit |
407 | C<root/lib/site/layout>, updating it to match the following (the two HTML |
408 | C<span> elements are new): |
409 | |
410 | <div id="header">[% PROCESS site/header %]</div> |
411 | |
412 | <div id="content"> |
413 | <span class="message">[% status_msg %]</span> |
414 | <span class="error">[% error_msg %]</span> |
415 | [% content %] |
416 | </div> |
417 | |
418 | <div id="footer">[% PROCESS site/footer %]</div> |
419 | |
420 | If we set either message in the Catalyst stash (e.g., |
421 | C<$c-E<gt>stash-E<gt>{status_msg} = 'Request was successful!'>) it will |
422 | be displayed whenever any view used by that request is rendered. The |
423 | C<message> and C<error> CSS styles are automatically defined in |
424 | C<root/src/ttsite.css> and can be customized to suit your needs. |
425 | |
426 | B<Note:> The Catalyst stash only lasts for a single HTTP request. If |
427 | you need to retain information across requests you can use |
428 | L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> (we will use |
429 | Catalyst sessions in the Authentication part of the tutorial). |
430 | |
431 | |
432 | =head2 Create a TT Template Page |
433 | |
434 | To add a new page of content to the TTSite view hierarchy, just create a |
435 | new C<.tt2> file in C<root/src>. Only include HTML markup that goes |
436 | inside the HTML <body> and </body> tags, TTSite will use the contents of |
437 | C<root/lib/site> to add the top and bottom. |
438 | |
439 | First create a directory for book-related TT templates: |
440 | |
441 | $ mkdir root/src/books |
442 | |
443 | Then create C<root/src/books/list.tt2> in your editor and enter: |
444 | |
445 | [% # This is a TT comment. The '-' at the end "chomps" the newline. You won't -%] |
446 | [% # see this "chomping" in your browser because HTML ignores blank lines, but -%] |
447 | [% # it WILL eliminate a blank line if you view the HTML source. It's purely -%] |
448 | [%- # optional, but both the beginning and the ending TT tags support chomping. -%] |
449 | |
450 | [% # Provide a title to root/lib/site/header -%] |
451 | [% META title = 'Book List' -%] |
452 | |
453 | <table> |
454 | <tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr> |
455 | [% # Display each book in a table row %] |
456 | [% FOREACH book IN books -%] |
457 | <tr> |
458 | <td>[% book.title %]</td> |
459 | <td>[% book.rating %]</td> |
460 | </tr> |
461 | [% END -%] |
462 | </table> |
463 | |
464 | As indicated by the inline comments above, the C<META title> line uses |
465 | TT's META feature to provide a title to C<root/lib/site/header>. |
466 | Meanwhile, the outer C<FOREACH> loop iterates through each C<book> model |
467 | object and prints the C<title> and C<rating> fields. An inner |
468 | C<FOREACH> loop prints the last name of each author in a comma-separated |
469 | list within a single table cell. |
470 | |
471 | If you are new to TT, the C<[%> and C<%]> tags are used to delimit TT |
472 | code. TT supports a wide variety of directives for "calling" other |
473 | files, looping, conditional logic, etc. In general, TT simplifies the |
474 | usual range of Perl operators down to the single dot (C<.>) operator. |
475 | This applies to operations as diverse as method calls, hash lookups, and |
476 | list index values (see |
477 | L<http://www.template-toolkit.org/docs/default/Manual/Variables.html> |
478 | for details and examples). In addition to the usual C<Template> module |
479 | Pod documentation, you can access the TT manual at |
480 | L<http://www.template-toolkit.org/docs/default/>. |
481 | |
482 | B<NOTE>: The C<TTSite> helper creates several TT files using an |
483 | extension of C<.tt2>. Most other Catalyst and TT examples use an |
484 | extension of C<.tt>. You can use either extension (or no extension at |
485 | all) with TTSite and TT, just be sure to use the appropriate extension |
486 | for both the file itself I<and> the C<$c-E<gt>stash-E<gt>{template} = |
487 | ...> line in your controller. This document will use C<.tt2> for |
488 | consistency with the files already created by the C<TTSite> helper. |
489 | |
490 | |
491 | =head1 CREATE A SQLITE DATABASE |
492 | |
493 | In this step, we make a text file with the required SQL commands to |
494 | create a database table and load some sample data. Open C<myapp01.sql> |
495 | in your editor and enter: |
496 | |
497 | -- |
498 | -- Create a very simple database to hold book and author information |
499 | -- |
500 | CREATE TABLE books ( |
501 | id INTEGER PRIMARY KEY, |
502 | title TEXT , |
503 | rating INTEGER |
504 | ); |
505 | -- 'book_authors' is a many-to-many join table between books & authors |
506 | CREATE TABLE book_authors ( |
507 | book_id INTEGER, |
508 | author_id INTEGER, |
509 | PRIMARY KEY (book_id, author_id) |
510 | ); |
511 | CREATE TABLE authors ( |
512 | id INTEGER PRIMARY KEY, |
513 | first_name TEXT, |
514 | last_name TEXT |
515 | ); |
516 | --- |
517 | --- Load some sample data |
518 | --- |
519 | INSERT INTO books VALUES (1, 'CCSP SNRS Exam Certification Guide', 5); |
520 | INSERT INTO books VALUES (2, 'TCP/IP Illustrated, Volume 1', 5); |
521 | INSERT INTO books VALUES (3, 'Internetworking with TCP/IP Vol.1', 4); |
522 | INSERT INTO books VALUES (4, 'Perl Cookbook', 5); |
523 | INSERT INTO books VALUES (5, 'Designing with Web Standards', 5); |
524 | INSERT INTO authors VALUES (1, 'Greg', 'Bastien'); |
525 | INSERT INTO authors VALUES (2, 'Sara', 'Nasseh'); |
526 | INSERT INTO authors VALUES (3, 'Christian', 'Degu'); |
527 | INSERT INTO authors VALUES (4, 'Richard', 'Stevens'); |
528 | INSERT INTO authors VALUES (5, 'Douglas', 'Comer'); |
529 | INSERT INTO authors VALUES (6, 'Tom', 'Christiansen'); |
530 | INSERT INTO authors VALUES (7, 'Nathan', 'Torkington'); |
531 | INSERT INTO authors VALUES (8, 'Jeffrey', 'Zeldman'); |
532 | INSERT INTO book_authors VALUES (1, 1); |
533 | INSERT INTO book_authors VALUES (1, 2); |
534 | INSERT INTO book_authors VALUES (1, 3); |
535 | INSERT INTO book_authors VALUES (2, 4); |
536 | INSERT INTO book_authors VALUES (3, 5); |
537 | INSERT INTO book_authors VALUES (4, 6); |
538 | INSERT INTO book_authors VALUES (4, 7); |
539 | INSERT INTO book_authors VALUES (5, 8); |
540 | |
541 | B<TIP>: See Appendix 1 for tips on removing the leading spaces when |
542 | cutting and pasting example code from POD-based documents. |
543 | |
544 | Then use the following command to build a C<myapp.db> SQLite database: |
545 | |
546 | $ sqlite3 myapp.db < myapp01.sql |
547 | |
548 | If you need to create the database more than once, you probably want to |
549 | issue the C<rm myapp.db> command to delete the database before you use |
550 | the C<sqlite3 myapp.db < myapp01.sql> command. |
551 | |
552 | Once the C<myapp.db> database file has been created and initialized, you |
553 | can use the SQLite command line environment to do a quick dump of the |
554 | database contents: |
555 | |
556 | $ sqlite3 myapp.db |
557 | SQLite version 3.4.2 |
558 | Enter ".help" for instructions |
559 | sqlite> select * from books; |
560 | 1|CCSP SNRS Exam Certification Guide|5 |
561 | 2|TCP/IP Illustrated, Volume 1|5 |
562 | 3|Internetworking with TCP/IP Vol.1|4 |
563 | 4|Perl Cookbook|5 |
564 | 5|Designing with Web Standards|5 |
565 | sqlite> .q |
566 | $ |
567 | |
568 | Or: |
569 | |
570 | $ sqlite3 myapp.db "select * from books" |
571 | 1|CCSP SNRS Exam Certification Guide|5 |
572 | 2|TCP/IP Illustrated, Volume 1|5 |
573 | 3|Internetworking with TCP/IP Vol.1|4 |
574 | 4|Perl Cookbook|5 |
575 | 5|Designing with Web Standards|5 |
576 | |
577 | As with most other SQL tools, if you are using the full "interactive" |
578 | environment you need to terminate your SQL commands with a ";" (it's not |
579 | required if you do a single SQL statement on the command line). Use |
580 | ".q" to exit from SQLite from the SQLite interactive mode and return to |
581 | your OS command prompt. |
582 | |
583 | |
584 | =head1 DATABASE ACCESS WITH C<DBIx::Class> |
585 | |
586 | Catalyst can be used with virtually any form of persistent datastore |
587 | available via Perl. For example, |
588 | L<Catalyst::Model::DBI|Catalyst::Model::DBI> can be used to |
589 | easily access databases through the traditional Perl C<DBI> interface. |
590 | However, most Catalyst applications use some form of ORM technology to |
591 | automatically create and save model objects as they are used. Although |
592 | Tony Bowden's L<Class::DBI|Class::DBI> has been a popular choice |
593 | in the past, Matt Trout's L<DBIx::Class|DBIx::Class> (abbreviated |
594 | as "DBIC") has rapidly emerged as the Perl-based ORM technology of choice. |
595 | Most new Catalyst applications rely on DBIC, as will this tutorial. |
596 | |
bb2dbfb8 |
597 | =head2 Create a dynamic DBIC Model |
3533daff |
598 | |
bb2dbfb8 |
599 | Use the C<create=dynamic> model helper option to build a model that |
3533daff |
600 | dynamically reads your database structure every time the application |
601 | starts: |
602 | |
bb2dbfb8 |
603 | $ script/myapp_create.pl model MyAppDB DBIC::Schema MyApp::Schema::MyAppDB create=dynamic dbi:SQLite:myapp.db |
3533daff |
604 | exists "/home/me/MyApp/script/../lib/MyApp/Model" |
605 | exists "/home/me/MyApp/script/../t" |
606 | created "/home/me/MyApp/script/../lib/MyApp/Schema" |
607 | created "/home/me/MyApp/script/../lib/MyApp/Schema/MyAppDB.pm" |
608 | created "/home/me/MyApp/script/../lib/MyApp/Model/MyAppDB.pm" |
609 | created "/home/me/MyApp/script/../t/model_MyAppDB.t" |
610 | |
611 | |
612 | C<MyAppDB> is the name of the model class to be created by the helper in |
613 | C<lib/MyApp/Model> (Catalyst has a separate directory under C<lib/MyApp> |
614 | for each of the three parts of MVC: C<Model>, C<View>, and C<Controller> |
615 | [although older Catalyst applications often use the directories C<M>, |
616 | C<V>, and C<C>]). C<DBIC::Schema> is the type of the model to create. |
617 | C<MyApp::Schema::MyAppDB> is the name of the DBIC schema file written to |
618 | C<lib/MyApp/Schema/MyAppDB.pm>. Because we specified C<create=dynamic> |
619 | to the helper, it use L<DBIx::Class::Schema::Loader> to dynamically load |
620 | the schema information from the database every time the application |
621 | starts. And finally, C<dbi:SQLite:myapp.db> is the standard DBI connect |
622 | string for use with SQLite. |
623 | |
19c49089 |
624 | B<NOTE>: Although the C<create=dynamic> option to the DBIC helper |
625 | makes for a nifty demonstration, is only really suitable for very |
626 | small applications. After this demonstration, you should almost always |
627 | use the C<create=static> option that we switch to below. |
dc9a0503 |
628 | |
629 | |
3533daff |
630 | =head1 RUN THE APPLICATION |
631 | |
632 | First, let's enable an environment variable option that causes |
633 | DBIx::Class to dump the SQL statements it's using to access the database |
634 | (this option can provide extremely helpful troubleshooting information): |
635 | |
636 | $ export DBIC_TRACE=1 |
637 | |
638 | This assumes you are using BASH as your shell -- adjust accordingly if |
639 | you are using a different shell (for example, under tcsh, use |
640 | C<setenv DBIC_TRACE 1>). |
641 | |
642 | B<NOTE>: You can also set this in your code using |
643 | C<$class-E<gt>storage-E<gt>debug(1);>. See |
644 | L<DBIx::Class::Manual::Troubleshooting> for details (including options |
645 | to log to file instead of displaying to the Catalyst development server |
646 | log). |
647 | |
648 | Then run the Catalyst "demo server" script: |
649 | |
650 | $ script/myapp_server.pl |
651 | |
652 | Your development server log output should display something like: |
653 | |
654 | $script/myapp_server.pl |
655 | [debug] Debug messages enabled |
656 | [debug] Loaded plugins: |
657 | .----------------------------------------------------------------------------. |
658 | | Catalyst::Plugin::ConfigLoader 0.17 | |
659 | | Catalyst::Plugin::StackTrace 0.06 | |
660 | | Catalyst::Plugin::Static::Simple 0.20 | |
661 | '----------------------------------------------------------------------------' |
662 | |
663 | [debug] Loaded dispatcher "Catalyst::Dispatcher" |
664 | [debug] Loaded engine "Catalyst::Engine::HTTP" |
665 | [debug] Found home "/home/me/MyApp" |
45d511e0 |
666 | [debug] Loaded Config "/home/me/MyApp/myapp.conf" |
3533daff |
667 | [debug] Loaded components: |
668 | .-----------------------------------------------------------------+----------. |
669 | | Class | Type | |
670 | +-----------------------------------------------------------------+----------+ |
671 | | MyApp::Controller::Books | instance | |
672 | | MyApp::Controller::Root | instance | |
673 | | MyApp::Model::MyAppDB | instance | |
674 | | MyApp::Model::MyAppDB::Authors | class | |
675 | | MyApp::Model::MyAppDB::BookAuthors | class | |
676 | | MyApp::Model::MyAppDB::Books | class | |
677 | | MyApp::View::TT | instance | |
678 | '-----------------------------------------------------------------+----------' |
679 | |
680 | [debug] Loaded Private actions: |
681 | .----------------------+--------------------------------------+--------------. |
682 | | Private | Class | Method | |
683 | +----------------------+--------------------------------------+--------------+ |
684 | | /default | MyApp::Controller::Root | default | |
685 | | /end | MyApp::Controller::Root | end | |
686 | | /books/index | MyApp::Controller::Books | index | |
687 | | /books/list | MyApp::Controller::Books | list | |
688 | '----------------------+--------------------------------------+--------------' |
689 | |
690 | [debug] Loaded Path actions: |
691 | .-------------------------------------+--------------------------------------. |
692 | | Path | Private | |
693 | +-------------------------------------+--------------------------------------+ |
694 | | /books/list | /books/list | |
695 | '-------------------------------------+--------------------------------------' |
696 | |
697 | [info] MyApp powered by Catalyst 5.7011 |
698 | You can connect to your server at http://localhost:3000 |
699 | |
700 | B<NOTE>: Be sure you run the C<script/myapp_server.pl> command from |
701 | the 'base' directory of your application, not inside the C<script> |
702 | directory itself or it will not be able to locate the C<myapp.db> |
703 | database file. You can use a fully qualified or a relative path to |
704 | locate the database file, but we did not specify that when we ran the |
705 | model helper earlier. |
706 | |
707 | Some things you should note in the output above: |
708 | |
709 | =over 4 |
710 | |
711 | =item * |
712 | |
713 | Catalyst::Model::DBIC::Schema dynamically created three model classes, |
714 | one to represent each of the three tables in our database |
715 | (C<MyApp::Model::MyAppDB::Authors>, C<MyApp::Model::MyAppDB::BookAuthors>, |
716 | and C<MyApp::Model::MyAppDB::Books>). |
717 | |
718 | =item * |
719 | |
720 | The "list" action in our Books controller showed up with a path of |
721 | C</books/list>. |
722 | |
723 | =back |
724 | |
725 | Point your browser to L<http://localhost:3000> and you should still get |
726 | the Catalyst welcome page. |
727 | |
728 | Next, to view the book list, change the URL in your browser to |
729 | L<http://localhost:3000/books/list>. You should get a list of the five |
730 | books loaded by the C<myapp01.sql> script above, with TTSite providing |
731 | the formatting for the very simple output we generated in our template. |
732 | The rating for each book should appear on each row. |
733 | |
734 | Also notice in the output of the C<script/myapp_server.pl> that DBIC |
735 | used the following SQL to retrieve the data: |
736 | |
737 | SELECT me.id, me.title, me.rating FROM books me |
738 | |
739 | because we enabled DBIC_TRACE. |
740 | |
741 | You now the beginnings of a simple but workable web application. |
742 | Continue on to future sections and we will develop the application |
743 | more fully. |
744 | |
745 | |
746 | =head1 A STATIC DATABASE MODEL WITH C<DBIx::Class> |
747 | |
748 | =head2 Create Static DBIC Schema Files |
749 | |
750 | Unlike the previous section where we had DBIC automatically discover the |
751 | structure of the database every time the application started, here we |
752 | will use static schema files for more control. This is typical of most |
753 | "real world" applications. |
754 | |
755 | One option would be to create a separate schema file for each table in |
756 | the database, however, lets use the same L<DBIx::Class::Schema::Loader> |
757 | used earlier with C<create=dynamic> to build the static files for us. |
758 | First, lets remove the schema file created in Part 2: |
759 | |
760 | $ rm lib/MyApp/Schema/MyAppDB.pm |
761 | |
762 | Now regenerate the schema using the C<create=static> option: |
763 | |
764 | $ script/myapp_create.pl model MyAppDB DBIC::Schema MyApp::Schema::MyAppDB create=static dbi:SQLite:myapp.db |
765 | exists "/home/me/MyApp/script/../lib/MyApp/Model" |
766 | exists "/home/me/MyApp/script/../t" |
767 | Dumping manual schema for MyApp::Schema::MyAppDB to directory /home/me/MyApp/script/../lib ... |
768 | Schema dump completed. |
769 | exists "/home/me/MyApp/script/../lib/MyApp/Model/MyAppDB.pm" |
770 | |
771 | We could have also deleted C<lib/MyApp/Model/MyAppDB.pm>, but it would |
772 | have regenerated the same file (note the C<exists> in the output above). |
773 | If you take a look at C<lib/MyApp/Model/MyAppDB.pm>, it simply contains |
774 | a reference to the actual schema file in C<lib/MyApp/Schema/MyAppDB.pm> |
775 | along with the database connect string. |
776 | |
777 | If you look in the C<lib/MyApp/Schema> directory, you will find that |
778 | C<MyAppDB.pm> is no longer using L<DBIx::Class::Schema::Loader> as its |
779 | base class (L<DBIx::Class::Schema::Loader> is only being used by the |
780 | helper to load the schema once and then create the static files for us) |
781 | and that it only contains a call to the C<load_classes> method. You |
782 | will also find that C<lib/MyApp/Schema> contains a C<MyAppDB> |
783 | subdirectory, with one file inside this directory for each of the tables |
784 | in our simple database (C<Authors.pm>, C<BookAuthors.pm>, and |
785 | C<Books.pm>). These three files were created based on the information |
786 | found by L<DBIx::Class::Schema::Loader> as the helper ran. |
787 | |
788 | The idea with all of the files created under C<lib/MyApp/Schema> by the |
789 | C<create=static> option is to only edit the files below the C<# DO NOT |
790 | MODIFY THIS OR ANYTHING ABOVE!> warning. If you place all of your |
791 | changes below that point in the file, you can regenerate the |
792 | auto-generated information at the top of each file should your database |
793 | structure get updated. |
794 | |
795 | Also note the "flow" of the model information across the various files |
796 | and directories. Catalyst will initially load the model from |
797 | C<lib/MyApp/Model/MyAppDB.pm>. This file contains a reference to |
798 | C<lib/MyApp/Schema/MyAppDB.pm>, so that file is loaded next. Finally, |
799 | the call to C<load_classes> in that file will load each of the |
800 | table-specific "results source" files from the C<lib/MyApp/Schema/MyAppDB> |
801 | subdirectory. These three table-specific DBIC schema files will then be |
802 | used to create three table-specific Catalyst models every time the |
803 | application starts (you can see these three model files listed in |
804 | the debug output generated when you launch the application). |
805 | |
806 | |
807 | =head2 Updating the Generated DBIC Schema Files |
808 | |
809 | |
810 | Let's manually add some relationship information to the auto-generated |
811 | schema files. First edit C<lib/MyApp/Schema/MyAppDB/Books.pm> and |
812 | add the following text below the C<# You can replace this text...> |
813 | comment: |
814 | |
815 | # |
816 | # Set relationships: |
817 | # |
818 | |
819 | # has_many(): |
820 | # args: |
821 | # 1) Name of relationship, DBIC will create accessor with this name |
822 | # 2) Name of the model class referenced by this relationship |
823 | # 3) Column name in *foreign* table |
824 | __PACKAGE__->has_many(book_authors => 'MyApp::Schema::MyAppDB::BookAuthors', 'book_id'); |
825 | |
826 | # many_to_many(): |
827 | # args: |
828 | # 1) Name of relationship, DBIC will create accessor with this name |
829 | # 2) Name of has_many() relationship this many_to_many() is shortcut for |
830 | # 3) Name of belongs_to() relationship in model class of has_many() above |
831 | # You must already have the has_many() defined to use a many_to_many(). |
832 | __PACKAGE__->many_to_many(authors => 'book_authors', 'author'); |
833 | |
834 | |
835 | B<Note:> Be careful to put this code I<above> the C<1;> at the end of the |
836 | file. As with any Perl package, we need to end the last line with |
837 | a statement that evaluates to C<true>. This is customarily done with |
838 | C<1;> on a line by itself. |
839 | |
840 | This code defines both a C<has_many> and a C<many_to_many> relationship. |
841 | The C<many_to_many> relationship is optional, but it makes it easier to |
842 | map a book to its collection of authors. Without it, we would have to |
843 | "walk" though the C<book_authors> table as in C<$book-E<gt>book_authors- |
844 | E<gt>first-E<gt>author-E<gt>last_name> (we will see examples on how to |
845 | use DBIC objects in your code soon, but note that because C<$book- |
846 | E<gt>book_authors> can return multiple authors, we have to use C<first> |
847 | to display a single author). C<many_to_many> allows us to use the |
848 | shorter C<$book-E<gt>authors-E<gt>first-E<gt>last_name>. Note that you |
849 | cannot define a C<many_to_many> relationship without also having the |
850 | C<has_many> relationship in place. |
851 | |
852 | Then edit C<lib/MyApp/Schema/MyAppDB/Authors.pm> and add relationship |
853 | information as follows (again, be careful to put in above the C<1;> but |
854 | below the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment): |
855 | |
856 | # |
857 | # Set relationships: |
858 | # |
859 | |
860 | # has_many(): |
861 | # args: |
862 | # 1) Name of relationship, DBIC will create accessor with this name |
863 | # 2) Name of the model class referenced by this relationship |
864 | # 3) Column name in *foreign* table |
865 | __PACKAGE__->has_many(book_author => 'MyApp::Schema::MyAppDB::BookAuthors', 'author_id'); |
866 | |
867 | # many_to_many(): |
868 | # args: |
869 | # 1) Name of relationship, DBIC will create accessor with this name |
870 | # 2) Name of has_many() relationship this many_to_many() is shortcut for |
871 | # 3) Name of belongs_to() relationship in model class of has_many() above |
872 | # You must already have the has_many() defined to use a many_to_many(). |
873 | __PACKAGE__->many_to_many(books => 'book_author', 'book'); |
874 | |
875 | Finally, do the same for the "join table," |
876 | C<lib/MyApp/Schema/MyAppDB/BookAuthors.pm>: |
877 | |
878 | # |
879 | # Set relationships: |
880 | # |
881 | |
882 | # belongs_to(): |
883 | # args: |
884 | # 1) Name of relationship, DBIC will create accessor with this name |
885 | # 2) Name of the model class referenced by this relationship |
886 | # 3) Column name in *this* table |
887 | __PACKAGE__->belongs_to(book => 'MyApp::Schema::MyAppDB::Books', 'book_id'); |
888 | |
889 | # belongs_to(): |
890 | # args: |
891 | # 1) Name of relationship, DBIC will create accessor with this name |
892 | # 2) Name of the model class referenced by this relationship |
893 | # 3) Column name in *this* table |
894 | __PACKAGE__->belongs_to(author => 'MyApp::Schema::MyAppDB::Authors', 'author_id'); |
895 | |
896 | |
897 | =head1 RUN THE APPLICATION |
898 | |
899 | Run the Catalyst "demo server" script with the C<DBIC_TRACE> option |
900 | (it might still be enabled from earlier in the tutorial, but here |
901 | is an alternate way to specify the option just in case): |
902 | |
903 | $ DBIC_TRACE=1 script/myapp_server.pl |
904 | |
905 | Make sure that the application loads correctly and that you see the |
906 | three dynamically created model class (one for each of the |
907 | table-specific schema classes we created). |
908 | |
909 | Then hit the URL L<http://localhost:3000/books/list> and be sure that |
910 | the book list is displayed. |
911 | |
912 | |
913 | =head1 RUNNING THE APPLICATION FROM THE COMMAND LINE |
914 | |
915 | In some situations, it can be useful to run your application and |
916 | display a page without using a browser. Catalyst lets you do this |
917 | using the C<scripts/myapp_test.pl> script. Just supply the URL you |
918 | wish to display and it will run that request through the normal |
919 | controller dispatch logic and use the appropriate view to render the |
920 | output (obviously, complex pages may dump a lot of text to your |
921 | terminal window). For example, if you type: |
922 | |
923 | $ script/myapp_test.pl "/books/list" |
924 | |
925 | You should get the same text as if you visited |
926 | L<http://localhost:3000/books/list> with the normal development server |
927 | and asked your browser to view the page source. |
928 | |
929 | |
930 | =head1 UPDATING THE VIEW |
931 | |
932 | Let's add a new column to our book list page that takes advantage of |
933 | the relationship information we manually added to our schema files |
934 | in the previous section. Edit C<root/src/books/list.tt2> add add the |
935 | following code below the existing table cell that contains |
936 | C<book.rating> (IOW, add a new table cell below the existing two |
937 | C<td> cells): |
938 | |
939 | <td> |
940 | [% # First initialize a TT variable to hold a list. Then use a TT FOREACH -%] |
941 | [% # loop in 'side effect notation' to load just the last names of the -%] |
942 | [% # authors into the list. Note that the 'push' TT vmethod does not -%] |
943 | [% # a value, so nothing will be printed here. But, if you have something -%] |
944 | [% # in TT that does return a method and you don't want it printed, you -%] |
945 | [% # can: 1) assign it to a bogus value, or 2) use the CALL keyword to -%] |
946 | [% # call it and discard the return value. -%] |
947 | [% tt_authors = [ ]; |
948 | tt_authors.push(author.last_name) FOREACH author = book.authors %] |
949 | [% # Now use a TT 'virtual method' to display the author count in parens -%] |
950 | [% # Note the use of the TT filter "| html" to escape dangerous characters -%] |
951 | ([% tt_authors.size | html %]) |
952 | [% # Use another TT vmethod to join & print the names & comma separators -%] |
953 | [% tt_authors.join(', ') | html %] |
954 | </td> |
955 | |
956 | Then hit C<Ctrl+R> in your browser (not that you don't need to reload |
957 | the development server or use the C<-r> option when updating TT |
958 | templates) and you should now the the number of authors each book and |
959 | a comma-separated list of the author's last names. |
960 | |
961 | If you are still running the development server with C<DBIC_TRACE> |
962 | enabled, you should also now see five more C<SELECT> statements in the |
963 | debug output (one for each book as the authors are being retrieved by |
964 | DBIC). |
965 | |
966 | Also note that we are using "| html", a type of TT filter, to escape |
967 | characters such as E<lt> and E<gt> to < and > and avoid various |
968 | types of dangerous hacks against your application. In a real |
969 | application, you would probably want to put "| html" at the end of |
970 | every field where a user has control over the information that can |
971 | appear in that field (and can therefore inject markup or code if you |
972 | don't "neutralize" those fields). In addition to "| html", Template |
973 | Toolkit has a variety of other useful filters that can found in the |
974 | documentation for L<Template::Filters|Template::Filters>. |
975 | |
976 | |
977 | =head2 Using C<RenderView> for the Default View |
978 | |
979 | B<NOTE: The rest of this part of the tutorial is optional. You can |
980 | skip to Part 4, L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>, |
981 | if you wish.> |
982 | |
983 | Once your controller logic has processed the request from a user, it |
984 | forwards processing to your view in order to generate the appropriate |
985 | response output. Catalyst uses |
986 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> by |
987 | default to automatically performs this operation. If you look in |
988 | C<lib/MyApp/Controller/Root.pm>, you should see the empty |
989 | definition for the C<sub end> method: |
990 | |
991 | sub end : ActionClass('RenderView') {} |
992 | |
993 | The following bullet points provide a quick overview of the |
994 | C<RenderView> process: |
995 | |
996 | =over 4 |
997 | |
998 | =item * |
999 | |
1000 | C<Root.pm> is designed to hold application-wide logic. |
1001 | |
1002 | =item * |
1003 | |
1004 | At the end of a given user request, Catalyst will call the most specific |
1005 | C<end> method that's appropriate. For example, if the controller for a |
1006 | request has an C<end> method defined, it will be called. However, if |
1007 | the controller does not define a controller-specific C<end> method, the |
1008 | "global" C<end> method in C<Root.pm> will be called. |
1009 | |
1010 | =item * |
1011 | |
1012 | Because the definition includes an C<ActionClass> attribute, the |
1013 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> logic |
1014 | will be executed B<after> any code inside the definition of C<sub end> |
1015 | is run. See L<Catalyst::Manual::Actions|Catalyst::Manual::Actions> |
1016 | for more information on C<ActionClass>. |
1017 | |
1018 | =item * |
1019 | |
1020 | Because C<sub end> is empty, this effectively just runs the default |
1021 | logic in C<RenderView>. However, you can easily extend the |
1022 | C<RenderView> logic by adding your own code inside the empty method body |
1023 | (C<{}>) created by the Catalyst Helpers when we first ran the |
1024 | C<catalyst.pl> to initialize our application. See |
1025 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> for more |
1026 | detailed information on how to extended C<RenderView> in C<sub end>. |
1027 | |
1028 | =back |
1029 | |
1030 | |
1031 | =head2 Using The Default Template Name |
1032 | |
1033 | By default, C<Catalyst::View::TT> will look for a template that uses the |
1034 | same name as your controller action, allowing you to save the step of |
1035 | manually specifying the template name in each action. For example, this |
1036 | would allow us to remove the |
1037 | C<$c-E<gt>stash-E<gt>{template} = 'books/list.tt2';> line of our |
1038 | C<list> action in the Books controller. Open |
1039 | C<lib/MyApp/Controller/Books.pm> in your editor and comment out this line |
1040 | to match the following (only the C<$c-E<gt>stash-E<gt>{template}> line |
1041 | has changed): |
1042 | |
1043 | =head2 list |
1044 | |
1045 | Fetch all book objects and pass to books/list.tt2 in stash to be displayed |
1046 | |
1047 | =cut |
1048 | |
1049 | sub list : Local { |
1050 | # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst |
1051 | # 'Context' that's used to 'glue together' the various components |
1052 | # that make up the application |
1053 | my ($self, $c) = @_; |
1054 | |
1055 | # Retrieve all of the book records as book model objects and store in the |
1056 | # stash where they can be accessed by the TT template |
1057 | $c->stash->{books} = [$c->model('MyAppDB::Books')->all]; |
1058 | |
1059 | # Set the TT template to use. You will almost always want to do this |
1060 | # in your action methods (actions methods respond to user input in |
1061 | # your controllers). |
1062 | #$c->stash->{template} = 'books/list.tt2'; |
1063 | } |
1064 | |
1065 | C<Catalyst::View::TT> defaults to looking for a template with no |
1066 | extension. In our case, we need to override this to look for an |
1067 | extension of C<.tt2>. Open C<lib/MyApp/View/TT.pm> and add the |
1068 | C<TEMPLATE_EXTENSION> definition as follows: |
1069 | |
1070 | __PACKAGE__->config({ |
1071 | CATALYST_VAR => 'Catalyst', |
1072 | INCLUDE_PATH => [ |
1073 | MyApp->path_to( 'root', 'src' ), |
1074 | MyApp->path_to( 'root', 'lib' ) |
1075 | ], |
1076 | PRE_PROCESS => 'config/main', |
1077 | WRAPPER => 'site/wrapper', |
1078 | ERROR => 'error.tt2', |
1079 | TIMER => 0, |
1080 | TEMPLATE_EXTENSION => '.tt2', |
1081 | }); |
1082 | |
1083 | You should now be able to restart the development server as per the |
1084 | previous section and access the L<http://localhost:3000/books/list> |
1085 | as before. |
1086 | |
1087 | B<NOTE:> Please note that if you use the default template technique, |
1088 | you will B<not> be able to use either the C<$c-E<gt>forward> or |
1089 | the C<$c-E<gt>detach> mechanisms (these are discussed in Part 2 and |
1090 | Part 9 of the Tutorial). |
1091 | |
1092 | |
1093 | =head2 Return To A Manually-Specified Template |
1094 | |
1095 | In order to be able to use C<$c-E<gt>forward> and C<$c-E<gt>detach> |
1096 | later in the tutorial, you should remove the comment from the |
1097 | statement in C<sub list> in C<lib/MyApp/Controller/Books.pm>: |
1098 | |
1099 | $c->stash->{template} = 'books/list.tt2'; |
1100 | |
1101 | Then delete the C<TEMPLATE_EXTENSION> line in |
1102 | C<lib/MyApp/View/TT.pm>. |
1103 | |
1104 | You should then be able to restart the development server and |
1105 | access L<http://localhost:3000/books/list> in the same manner as |
1106 | with earlier sections. |
1107 | |
1108 | |
1109 | =head1 AUTHOR |
1110 | |
1111 | Kennedy Clark, C<hkclark@gmail.com> |
1112 | |
1113 | Please report any errors, issues or suggestions to the author. The |
1114 | most recent version of the Catalyst Tutorial can be found at |
1115 | L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Manual/lib/Catalyst/Manual/Tutorial/>. |
1116 | |
1117 | Copyright 2006, Kennedy Clark, under Creative Commons License |
1118 | (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>). |
1119 | |