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