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 | |
1390ef0e |
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 |
3533daff |
65 | applications. |
66 | |
67 | You can checkout the source code for this example from the catalyst |
68 | subversion repository as per the instructions in |
1390ef0e |
69 | L<Catalyst::Manual::Tutorial::Intro|Catalyst::Manual::Tutorial::Intro>. |
3533daff |
70 | |
71 | |
72 | =head1 CREATE A NEW APPLICATION |
73 | |
1390ef0e |
74 | The remainder of the tutorial will build an application called C<MyApp>. |
75 | First use the Catalyst C<catalyst.pl> script to initialize the framework |
76 | for the C<MyApp> application (make sure you aren't still inside the |
77 | directory of the C<Hello> application from the previous part of the |
3533daff |
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 | |
1390ef0e |
89 | This creates a similar skeletal structure to what we saw in Part 2 of |
90 | the tutorial, except with C<MyApp> and C<myapp> substituted for |
3533daff |
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 |
1390ef0e |
97 | library of plugins and base classes available. Plugins are used to |
98 | seamlessly integrate existing Perl modules into the overall Catalyst |
99 | framework. In general, they do this by adding additional methods to the |
100 | C<context> object (generally written as C<$c>) that Catalyst passes to |
101 | every component throughout the framework. |
3533daff |
102 | |
103 | By default, Catalyst enables three plugins/flags: |
104 | |
105 | =over 4 |
106 | |
1390ef0e |
107 | =item * |
3533daff |
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 |
79a529cc |
113 | this item when you place your application into production. |
3533daff |
114 | |
1390ef0e |
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<__PACKAGE__-E<gt>setup> |
117 | line of your application class will be plugins, Catalyst supports a |
118 | limited number of flag options (of these, C<-Debug> is the most |
119 | common). See the documentation for C<Catalyst.pm> to get details on |
120 | other flags (currently C<-Engine>, C<-Home>, and C<-Log>). |
3533daff |
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 |
1390ef0e |
128 | server. We will not be using that approach in the tutorial, but feel |
3533daff |
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 | |
1390ef0e |
143 | B<IMPORTANT NOTE:> If you are using a version of |
144 | L<Catalyst::Devel|Catalyst::Devel> prior to version 1.06, you need to |
145 | be aware that Catalyst changed from a default format of YAML to the |
146 | more straightforward C<Config::General> format. This tutorial use the |
147 | newer C<myapp.conf> configuration file for C<Config::General> instead |
148 | of C<myapp.yml> for YAML. However, Catalyst has long supported both |
149 | formats and Catalyst will automatically use either C<myapp.conf> or |
150 | C<myapp.yml> (or any other format supported by |
056394af |
151 | L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> and |
1390ef0e |
152 | L<Config::Any|Config::Any>). If you are using a versions of |
153 | Catalyst::Devel prior to 1.06, you can convert to the newer format by |
154 | simply creating the C<myapp.yml> file manually and deleting |
155 | C<myapp.yml>. The default contents of C<myapp.conf> should only |
156 | consist of one line: C<name MyApp>. |
15e1d0b2 |
157 | |
1390ef0e |
158 | B<TIP>: This script can be useful for converting between configuration |
15e1d0b2 |
159 | formats: |
160 | |
1390ef0e |
161 | perl -Ilib -e 'use MyApp; use Config::General; |
15e1d0b2 |
162 | Config::General->new->save_file("myapp.conf", MyApp->config);' |
163 | |
d0496197 |
164 | B<NOTE:> The default C<myapp.conf> should look like: |
165 | |
166 | name MyApp |
15e1d0b2 |
167 | |
3533daff |
168 | =item * |
169 | |
170 | L<Catalyst::Plugin::Static::Simple|Catalyst::Plugin::Static::Simple> |
171 | |
172 | C<Static::Simple> provides an easy method of serving static content such |
173 | as images and CSS files under the development server. |
174 | |
175 | =back |
176 | |
94d8da41 |
177 | For our application, we want to add one new plugin into the mix. To |
1390ef0e |
178 | do this, edit C<lib/MyApp.pm> (this file is generally referred to as |
179 | your I<application class>) and delete the line with: |
3533daff |
180 | |
1390ef0e |
181 | __PACKAGE__->setup(qw/-Debug ConfigLoader Static::Simple/); |
3533daff |
182 | |
1390ef0e |
183 | Then replace it with: |
b411df01 |
184 | |
1390ef0e |
185 | __PACKAGE__->setup(qw/ |
186 | -Debug |
187 | ConfigLoader |
188 | Static::Simple |
189 | |
190 | StackTrace |
191 | /); |
192 | |
94d8da41 |
193 | B<Note:> Recent versions of C<Catalyst::Devel> have used a variety of |
194 | techniques to load these plugins/flags. If you are following along in |
195 | Ubuntu 8.10, you should have C<Catalyst::Devel> v1.07 and see the |
196 | default code shown above. If you are using v1.08, you should see the |
197 | following by default: |
198 | |
199 | use Catalyst qw/-Debug |
200 | ConfigLoader |
201 | Static::Simple/; |
202 | ... |
203 | __PACKAGE__->setup(); |
204 | |
205 | Don't let these variations confuse you -- they all accomplish the same |
206 | result. |
207 | |
1390ef0e |
208 | This tells Catalyst to start using one new plugin, |
209 | L<Catalyst::Plugin::StackTrace|Catalyst::Plugin::StackTrace>, to add a |
210 | stack trace to the standard Catalyst "debug screen" (the screen |
211 | Catalyst sends to your browser when an error occurs). Be aware that |
212 | L<StackTrace|Catalyst::Plugin::StackTrace> output appears in your |
213 | browser, not in the console window from which you're running your |
214 | application, which is where logging output usually goes. |
3533daff |
215 | |
1390ef0e |
216 | B<Notes:> |
3533daff |
217 | |
218 | =over 4 |
219 | |
1390ef0e |
220 | =item * |
221 | |
222 | C<__PACKAGE__> is just a shorthand way of referencing the name of the |
223 | package where it is used. Therefore, in C<MyApp.pm>, C<__PACKAGE__> |
224 | is equivalent to C<MyApp>. |
3533daff |
225 | |
1390ef0e |
226 | =item * |
3533daff |
227 | |
1390ef0e |
228 | You will want to disable L<StackTrace|Catalyst::Plugin::StackTrace> |
229 | before you put your application into production, but it can be helpful |
230 | during development. |
3533daff |
231 | |
1390ef0e |
232 | =item * |
3533daff |
233 | |
1390ef0e |
234 | When specifying plugins on the C<__PACKAGE__-E<gt>setup> line, you can |
235 | omit C<Catalyst::Plugin::> from the name. Additionally, you can |
236 | spread the plugin names across multiple lines as shown here, or place |
237 | them all on one (or more) lines as with the default configuration. |
cca5cd98 |
238 | |
3533daff |
239 | =back |
240 | |
3533daff |
241 | |
242 | =head1 CREATE A CATALYST CONTROLLER |
243 | |
1390ef0e |
244 | As discussed earlier, controllers are where you write methods that |
245 | interact with user input. Typically, controller methods respond to |
3533daff |
246 | C<GET> and C<POST> messages from the user's web browser. |
247 | |
248 | Use the Catalyst C<create> script to add a controller for book-related |
249 | actions: |
250 | |
251 | $ script/myapp_create.pl controller Books |
252 | exists "/home/me/MyApp/script/../lib/MyApp/Controller" |
253 | exists "/home/me/MyApp/script/../t" |
254 | created "/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm" |
255 | created "/home/me/MyApp/script/../t/controller_Books.t" |
256 | |
1390ef0e |
257 | Then edit C<lib/MyApp/Controller/Books.pm> (as discussed in Part 2 of |
258 | the Tutorial, Catalyst has a separate directory under C<lib/MyApp> for |
259 | each of the three parts of MVC: C<Model>, C<View>, and C<Controller>) |
260 | and add the following method to the controller: |
3533daff |
261 | |
262 | =head2 list |
263 | |
264 | Fetch all book objects and pass to books/list.tt2 in stash to be displayed |
265 | |
266 | =cut |
1390ef0e |
267 | |
3533daff |
268 | sub list : Local { |
269 | # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst |
270 | # 'Context' that's used to 'glue together' the various components |
271 | # that make up the application |
272 | my ($self, $c) = @_; |
273 | |
274 | # Retrieve all of the book records as book model objects and store in the |
275 | # stash where they can be accessed by the TT template |
1390ef0e |
276 | # $c->stash->{books} = [$c->model('DB::Books')->all]; |
277 | # But, for now, use this code until we create the model later |
278 | $c->stash->{books} = ''; |
279 | |
3533daff |
280 | # Set the TT template to use. You will almost always want to do this |
281 | # in your action methods (action methods respond to user input in |
282 | # your controllers). |
283 | $c->stash->{template} = 'books/list.tt2'; |
284 | } |
285 | |
1390ef0e |
286 | B<TIP>: See Appendix 1 for tips on removing the leading spaces when |
287 | cutting and pasting example code from POD-based documents. |
3533daff |
288 | |
1390ef0e |
289 | Programmers experienced with object-oriented Perl should recognize |
290 | C<$self> as a reference to the object where this method was called. |
291 | On the other hand, C<$c> will be new to many Perl programmers who have |
292 | not used Catalyst before (it's sometimes written as C<$context>). The |
293 | Context object is automatically passed to all Catalyst components. It |
294 | is used to pass information between components and provide access to |
295 | Catalyst and plugin functionality. |
3533daff |
296 | |
297 | B<Note:> Catalyst actions are regular Perl methods, but they make use |
1390ef0e |
298 | of Nicholas Clark's C<attributes> module (that's the "C<: Local>" next |
3533daff |
299 | to the C<sub list> in the code above) to provide additional |
300 | information to the Catalyst dispatcher logic. Many newer Catalyst |
ae492862 |
301 | applications are switching to the use of "Literal" C<:Path> actions |
3533daff |
302 | and C<Args> attribute in lieu of C<: Local> and C<: Private>. For |
1390ef0e |
303 | example, C<sub any_method :Path :Args(0)> can be used instead of C<sub |
304 | index :Private> (because no path was supplied to C<Path> it matches |
305 | the "empty" URL in the namespace of that module... the same thing |
306 | C<sub index> would do) or C<sub list :Path('list') :Args(0)> could be |
307 | used instead of the C<sub list : Local> above (the C<list> argument to |
308 | C<Path> would make it match on the URL C<list> under C<books>, the |
309 | namespace of the current module). See "Action Types" in |
3533daff |
310 | L<Catalyst::Manual::Intro|Catalyst::Manual::Intro> as well as Part 5 |
311 | of this tutorial (Authentication) for additional information. Another |
312 | popular but more advanced feature is C<Chained> actions that allow a |
313 | single URL to "chain together" multiple action method calls, each with |
1390ef0e |
314 | an appropriate number of arguments (see |
315 | L<Catalyst::DispatchType::Chained|Catalyst::DispatchType::Chained> for |
316 | details). |
3533daff |
317 | |
318 | |
319 | =head1 CATALYST VIEWS |
320 | |
321 | As mentioned in Part 2 of the tutorial, views are where you render |
1390ef0e |
322 | output, typically for display in the user's web browser (but also |
323 | possibly using other display output-generation systems). The code in |
324 | C<lib/MyApp/View> selects the I<type> of view to use, with the actual |
325 | rendering template found in the C<root> directory. As with virtually |
326 | every aspect of Catalyst, options abound when it comes to the specific |
327 | view technology you adopt inside your application. However, most |
328 | Catalyst applications use the Template Toolkit, known as TT (for more |
329 | information on TT, see L<http://www.template-toolkit.org>). Other |
330 | somewhat popular view technologies include Mason |
331 | (L<http://www.masonhq.com> and L<http://www.masonbook.com>) and |
332 | L<HTML::Template> (L<http://html-template.sourceforge.net>). |
333 | |
334 | |
335 | =head2 Create a Catalyst View |
3533daff |
336 | |
337 | When using TT for the Catalyst view, there are two main helper scripts: |
338 | |
339 | =over 4 |
340 | |
341 | =item * |
342 | |
343 | L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT> |
344 | |
345 | =item * |
346 | |
347 | L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite> |
348 | |
349 | =back |
350 | |
de966eb4 |
351 | Both helpers are similar. C<TT> creates the C<lib/MyApp/View/TT.pm> |
3533daff |
352 | file and leaves the creation of any hierarchical template organization |
353 | entirely up to you. (It also creates a C<t/view_TT.t> file for testing; |
de966eb4 |
354 | test cases will be discussed in Part 8.) C<TTSite>, on the other hand, |
355 | creates a modular and hierarchical view layout with |
1390ef0e |
356 | separate Template Toolkit (TT) files for common header and footer |
357 | information, configuration values, a CSS stylesheet, and more. |
358 | |
de966eb4 |
359 | While C<TTSite> was useful to bootstrap a project, its use is now |
360 | deprecated and to be considered historical. For most Catalyst |
361 | applications it adds redundant functionality and structure; many in the |
362 | Catalyst community recommend that it's easier to learn both Catalyst and |
363 | Template Toolkit if you use the more basic C<TT> approach. |
364 | Consequently, this tutorial will use "plain old TT." |
1390ef0e |
365 | |
366 | Enter the following command to enable the C<TT> style of view |
3533daff |
367 | rendering for this tutorial: |
368 | |
1390ef0e |
369 | $ script/myapp_create.pl view TT TT |
3533daff |
370 | exists "/home/me/MyApp/script/../lib/MyApp/View" |
371 | exists "/home/me/MyApp/script/../t" |
1390ef0e |
372 | created "/home/me/MyApp/script/../lib/MyApp/View/TT.pm" |
373 | created "/home/me/MyApp/script/../t/view_TT.t" |
3533daff |
374 | |
1390ef0e |
375 | This simply creates a view called C<TT> (the second 'TT' argument) in |
376 | a file called C<TT.pm> (the first 'TT' argument). It is now up to you |
377 | to decide how you want to structure your view layout. For the |
378 | tutorial, we will start with a very simple TT template to initially |
379 | demonstrate the concepts, but quickly migrate to a more typical |
380 | "wrapper page" type of configuration (where the "wrapper" controls the |
381 | overall "look and feel" of your site from a single file or set of |
382 | files). |
3533daff |
383 | |
1390ef0e |
384 | Edit C<lib/MyApp/View/TT.pm> and you should see that the default |
385 | contents contains something similar to the following: |
3533daff |
386 | |
1390ef0e |
387 | __PACKAGE__->config(TEMPLATE_EXTENSION => '.tt'); |
3533daff |
388 | |
1390ef0e |
389 | And update it to match: |
390 | |
391 | __PACKAGE__->config( |
392 | # Change default TT extension |
393 | TEMPLATE_EXTENSION => '.tt2', |
394 | # Set the location for TT files |
395 | INCLUDE_PATH => [ |
6abd3023 |
396 | MyApp->path_to( 'root', 'src' ), |
1390ef0e |
397 | ], |
398 | ); |
3533daff |
399 | |
1390ef0e |
400 | B<NOTE:> Make sure to add a comma after '.tt2' outside the single |
401 | quote. |
402 | |
191dee29 |
403 | This changes the default extension for Template Toolkit from '.tt' to |
1390ef0e |
404 | '.tt2' and changes the base directory for your template files from |
de966eb4 |
405 | C<root> to C<root/src>. These changes from the default are done mostly |
406 | to facilitate the application we're developing in this tutorial; as with |
407 | most things Perl, there's more than one way to do it... |
1390ef0e |
408 | |
409 | |
410 | =head2 Create a TT Template Page |
3533daff |
411 | |
412 | First create a directory for book-related TT templates: |
413 | |
1390ef0e |
414 | $ mkdir -p root/src/books |
3533daff |
415 | |
416 | Then create C<root/src/books/list.tt2> in your editor and enter: |
417 | |
418 | [% # This is a TT comment. The '-' at the end "chomps" the newline. You won't -%] |
419 | [% # see this "chomping" in your browser because HTML ignores blank lines, but -%] |
420 | [% # it WILL eliminate a blank line if you view the HTML source. It's purely -%] |
421 | [%- # optional, but both the beginning and the ending TT tags support chomping. -%] |
422 | |
1390ef0e |
423 | [% # Provide a title -%] |
3533daff |
424 | [% META title = 'Book List' -%] |
425 | |
426 | <table> |
427 | <tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr> |
428 | [% # Display each book in a table row %] |
429 | [% FOREACH book IN books -%] |
430 | <tr> |
431 | <td>[% book.title %]</td> |
432 | <td>[% book.rating %]</td> |
433 | </tr> |
434 | [% END -%] |
435 | </table> |
436 | |
437 | As indicated by the inline comments above, the C<META title> line uses |
1390ef0e |
438 | TT's META feature to provide a title to the "wrapper" that we will |
439 | create later. Meanwhile, the C<FOREACH> loop iterates through each |
440 | C<book> model object and prints the C<title> and C<rating> fields. |
3533daff |
441 | |
442 | If you are new to TT, the C<[%> and C<%]> tags are used to delimit TT |
443 | code. TT supports a wide variety of directives for "calling" other |
444 | files, looping, conditional logic, etc. In general, TT simplifies the |
445 | usual range of Perl operators down to the single dot (C<.>) operator. |
446 | This applies to operations as diverse as method calls, hash lookups, and |
447 | list index values (see |
55beb65d |
448 | L<http://search.cpan.org/perldoc?Template::Manual::Variables> |
3533daff |
449 | for details and examples). In addition to the usual C<Template> module |
450 | Pod documentation, you can access the TT manual at |
55beb65d |
451 | L<http://search.cpan.org/perldoc?Template::Manual>. |
3533daff |
452 | |
1390ef0e |
453 | B<TIP:> While you can build all sorts of complex logic into your TT |
454 | templates, you should in general keep the "code" part of your templates |
455 | as simple as possible. If you need more complex logic, create helper |
456 | methods in your model that abstract out a set of code into a single call |
457 | from your TT template. (Note that the same is true of your controller |
458 | logic as well -- complex sections of code in your controllers should |
459 | often be pulled out and placed into your model objects.) |
460 | |
461 | |
462 | =head2 Test Run The Application |
463 | |
464 | To test your work so far, first start the development server: |
465 | |
466 | $ script/myapp_server.pl |
467 | |
468 | Then point your browser to L<http://localhost:3000> and you should |
469 | still get the Catalyst welcome page. Next, change the URL in your |
470 | browser to L<http://localhost:3000/books/list>. If you have |
471 | everything working so far, you should see a web page that displays |
472 | nothing other than our column headers for "Title", "Rating", and |
473 | "Author(s)" -- we will not see any books until we get the database and |
474 | model working below. |
475 | |
476 | If you run into problems getting your application to run correctly, it |
477 | might be helpful to refer to some of the debugging techniques covered in |
478 | the L<Debugging|Catalyst::Manual::Tutorial::Debugging> part of the |
479 | tutorial. |
3533daff |
480 | |
481 | |
482 | =head1 CREATE A SQLITE DATABASE |
483 | |
484 | In this step, we make a text file with the required SQL commands to |
1390ef0e |
485 | create a database table and load some sample data. We will use SQLite, |
486 | a popular database that is lightweight and easy to use. Open |
487 | C<myapp01.sql> in your editor and enter: |
3533daff |
488 | |
489 | -- |
490 | -- Create a very simple database to hold book and author information |
491 | -- |
492 | CREATE TABLE books ( |
493 | id INTEGER PRIMARY KEY, |
494 | title TEXT , |
495 | rating INTEGER |
496 | ); |
497 | -- 'book_authors' is a many-to-many join table between books & authors |
498 | CREATE TABLE book_authors ( |
499 | book_id INTEGER, |
500 | author_id INTEGER, |
501 | PRIMARY KEY (book_id, author_id) |
502 | ); |
503 | CREATE TABLE authors ( |
504 | id INTEGER PRIMARY KEY, |
505 | first_name TEXT, |
506 | last_name TEXT |
507 | ); |
508 | --- |
509 | --- Load some sample data |
510 | --- |
511 | INSERT INTO books VALUES (1, 'CCSP SNRS Exam Certification Guide', 5); |
512 | INSERT INTO books VALUES (2, 'TCP/IP Illustrated, Volume 1', 5); |
513 | INSERT INTO books VALUES (3, 'Internetworking with TCP/IP Vol.1', 4); |
514 | INSERT INTO books VALUES (4, 'Perl Cookbook', 5); |
515 | INSERT INTO books VALUES (5, 'Designing with Web Standards', 5); |
516 | INSERT INTO authors VALUES (1, 'Greg', 'Bastien'); |
517 | INSERT INTO authors VALUES (2, 'Sara', 'Nasseh'); |
518 | INSERT INTO authors VALUES (3, 'Christian', 'Degu'); |
519 | INSERT INTO authors VALUES (4, 'Richard', 'Stevens'); |
520 | INSERT INTO authors VALUES (5, 'Douglas', 'Comer'); |
521 | INSERT INTO authors VALUES (6, 'Tom', 'Christiansen'); |
522 | INSERT INTO authors VALUES (7, 'Nathan', 'Torkington'); |
523 | INSERT INTO authors VALUES (8, 'Jeffrey', 'Zeldman'); |
524 | INSERT INTO book_authors VALUES (1, 1); |
525 | INSERT INTO book_authors VALUES (1, 2); |
526 | INSERT INTO book_authors VALUES (1, 3); |
527 | INSERT INTO book_authors VALUES (2, 4); |
528 | INSERT INTO book_authors VALUES (3, 5); |
529 | INSERT INTO book_authors VALUES (4, 6); |
530 | INSERT INTO book_authors VALUES (4, 7); |
531 | INSERT INTO book_authors VALUES (5, 8); |
532 | |
3533daff |
533 | Then use the following command to build a C<myapp.db> SQLite database: |
534 | |
535 | $ sqlite3 myapp.db < myapp01.sql |
536 | |
537 | If you need to create the database more than once, you probably want to |
538 | issue the C<rm myapp.db> command to delete the database before you use |
1390ef0e |
539 | the C<sqlite3 myapp.db E<lt> myapp01.sql> command. |
3533daff |
540 | |
541 | Once the C<myapp.db> database file has been created and initialized, you |
542 | can use the SQLite command line environment to do a quick dump of the |
543 | database contents: |
544 | |
545 | $ sqlite3 myapp.db |
546 | SQLite version 3.4.2 |
547 | Enter ".help" for instructions |
548 | sqlite> select * from books; |
549 | 1|CCSP SNRS Exam Certification Guide|5 |
550 | 2|TCP/IP Illustrated, Volume 1|5 |
551 | 3|Internetworking with TCP/IP Vol.1|4 |
552 | 4|Perl Cookbook|5 |
553 | 5|Designing with Web Standards|5 |
554 | sqlite> .q |
555 | $ |
556 | |
557 | Or: |
558 | |
559 | $ sqlite3 myapp.db "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 | |
566 | As with most other SQL tools, if you are using the full "interactive" |
567 | environment you need to terminate your SQL commands with a ";" (it's not |
568 | required if you do a single SQL statement on the command line). Use |
569 | ".q" to exit from SQLite from the SQLite interactive mode and return to |
570 | your OS command prompt. |
571 | |
572 | |
573 | =head1 DATABASE ACCESS WITH C<DBIx::Class> |
574 | |
191dee29 |
575 | Catalyst can be used with virtually any form of persistent datastore |
576 | available via Perl. For example, |
577 | L<Catalyst::Model::DBI|Catalyst::Model::DBI> can be used to easily |
578 | access databases through the traditional Perl C<DBI> interface. However, |
579 | most Catalyst applications use some form of ORM technology to |
580 | automatically create and save model objects as they are used. Although |
581 | L<Class::DBI|Class::DBI> has been a popular choice in the past, Matt |
582 | Trout's L<DBIx::Class|DBIx::Class> (abbreviated as "DBIC") has rapidly |
583 | emerged as the Perl-based ORM technology of choice. Most new Catalyst |
584 | applications rely on DBIC, as will this tutorial. |
3533daff |
585 | |
3533daff |
586 | |
1390ef0e |
587 | =head2 Create a Dynamic DBIC Model |
588 | |
589 | Use the C<create=dynamic> model helper option to build a model that |
3533daff |
590 | dynamically reads your database structure every time the application |
591 | starts: |
592 | |
d0496197 |
593 | $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema create=dynamic dbi:SQLite:myapp.db |
1390ef0e |
594 | exists "/home/me/MyApp/script/../lib/MyApp/Model" |
595 | exists "/home/me/MyApp/script/../t" |
596 | exists "/home/me/MyApp/script/../lib/MyApp" |
597 | created "/home/me/MyApp/script/../lib/MyApp/Schema.pm" |
598 | created "/home/me/MyApp/script/../lib/MyApp/Model/DB.pm" |
599 | created "/home/me/MyApp/script/../t/model_DB.t" |
3533daff |
600 | |
601 | |
d0496197 |
602 | C<DB> is the name of the model class to be created by the helper in |
1390ef0e |
603 | C<lib/MyApp/Model>. C<DBIC::Schema> is the type of the model to |
604 | create. C<MyApp::Schema> is the name of the DBIC schema file written |
605 | to C<lib/MyApp/Schema.pm>. Because we specified C<create=dynamic> to |
606 | the helper, it use |
607 | L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> to |
608 | dynamically load the schema information from the database every time |
c93b5eaa |
609 | the application starts. DBIC uses the schema to load other classes |
610 | that represent the tables in your database (DBIC refers to these |
611 | "table objects" as "result sources," see |
612 | L<DBIx::Class::ResultSource|DBIx::Class::ResultSource>). And finally, |
613 | C<dbi:SQLite:myapp.db> is the standard DBI connect string for use with |
614 | SQLite. |
3533daff |
615 | |
d0496197 |
616 | B<NOTE:> Although the C<create=dynamic> option to the DBIC helper |
19c49089 |
617 | makes for a nifty demonstration, is only really suitable for very |
618 | small applications. After this demonstration, you should almost always |
619 | use the C<create=static> option that we switch to below. |
dc9a0503 |
620 | |
621 | |
1390ef0e |
622 | =head1 ENABLE THE MODEL IN THE CONTROLLER |
623 | |
191dee29 |
624 | Open C<lib/MyApp/Controller/Books.pm> and un-comment the model code we |
625 | left disabled earlier (un-comment the line containing |
1390ef0e |
626 | C<[$c-E<gt>model('DB::Books')-E<gt>all]> and delete the next 2 lines): |
627 | |
628 | =head2 list |
629 | |
630 | Fetch all book objects and pass to books/list.tt2 in stash to be displayed |
631 | |
632 | =cut |
633 | |
634 | sub list : Local { |
635 | # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst |
636 | # 'Context' that's used to 'glue together' the various components |
637 | # that make up the application |
638 | my ($self, $c) = @_; |
639 | |
640 | # Retrieve all of the book records as book model objects and store in the |
641 | # stash where they can be accessed by the TT template |
642 | $c->stash->{books} = [$c->model('DB::Books')->all]; |
643 | |
644 | # Set the TT template to use. You will almost always want to do this |
645 | # in your action methods (action methods respond to user input in |
646 | # your controllers). |
647 | $c->stash->{template} = 'books/list.tt2'; |
648 | } |
649 | |
c93b5eaa |
650 | B<TIP>: You may see the C<$c-E<gt>model('DB::Book')> un-commented |
651 | above written as C<$c-E<gt>model('DB')-E<gt>resultset('Book')>. The |
652 | two are equivalent. Either way, C<$c-E<gt>model> returns a |
653 | L<DBIx::Class::ResultSet|DBIx::Class::ResultSet> which handles queries |
654 | against the database and iterating over the set of results that are |
655 | returned. |
656 | |
657 | We are using the C<-E<gt>all> to fetch all of the books. DBIC |
658 | supports a wide variety of more advanced operations to easily do |
659 | things like filtering and sorting the results. For example, the |
518f3851 |
660 | following could be used to sort the results by descending title: |
c93b5eaa |
661 | |
662 | $c->model('DB::Books')->search({}, {order_by => 'title DESC'}); |
663 | |
664 | Some other examples are provided in |
665 | L<DBIx::Class::Manual::Cookbook/Complex WHERE clauses>, with |
666 | additional information found at L<DBIx::Class::ResultSet/search>, |
667 | L<DBIx::Class::Manual::FAQ/Searching>, |
668 | L<DBIx::Class::Manual::Intro|DBIx::Class::Manual::Intro> |
669 | and L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema>. |
1390ef0e |
670 | |
671 | |
672 | =head2 Test Run The Application |
3533daff |
673 | |
674 | First, let's enable an environment variable option that causes |
675 | DBIx::Class to dump the SQL statements it's using to access the database |
676 | (this option can provide extremely helpful troubleshooting information): |
677 | |
678 | $ export DBIC_TRACE=1 |
679 | |
680 | This assumes you are using BASH as your shell -- adjust accordingly if |
681 | you are using a different shell (for example, under tcsh, use |
682 | C<setenv DBIC_TRACE 1>). |
683 | |
d0496197 |
684 | B<NOTE:> You can also set this in your code using |
3533daff |
685 | C<$class-E<gt>storage-E<gt>debug(1);>. See |
686 | L<DBIx::Class::Manual::Troubleshooting> for details (including options |
687 | to log to file instead of displaying to the Catalyst development server |
688 | log). |
689 | |
1390ef0e |
690 | Then launch the Catalyst development server. The log output should |
691 | display something like: |
3533daff |
692 | |
693 | $script/myapp_server.pl |
694 | [debug] Debug messages enabled |
1390ef0e |
695 | [debug] Statistics enabled |
3533daff |
696 | [debug] Loaded plugins: |
697 | .----------------------------------------------------------------------------. |
1390ef0e |
698 | | Catalyst::Plugin::ConfigLoader 0.20 | |
699 | | Catalyst::Plugin::StackTrace 0.08 | |
3533daff |
700 | | Catalyst::Plugin::Static::Simple 0.20 | |
701 | '----------------------------------------------------------------------------' |
702 | |
703 | [debug] Loaded dispatcher "Catalyst::Dispatcher" |
704 | [debug] Loaded engine "Catalyst::Engine::HTTP" |
705 | [debug] Found home "/home/me/MyApp" |
45d511e0 |
706 | [debug] Loaded Config "/home/me/MyApp/myapp.conf" |
3533daff |
707 | [debug] Loaded components: |
708 | .-----------------------------------------------------------------+----------. |
709 | | Class | Type | |
710 | +-----------------------------------------------------------------+----------+ |
711 | | MyApp::Controller::Books | instance | |
712 | | MyApp::Controller::Root | instance | |
d0496197 |
713 | | MyApp::Model::DB | instance | |
714 | | MyApp::Model::DB::Authors | class | |
715 | | MyApp::Model::DB::BookAuthors | class | |
716 | | MyApp::Model::DB::Books | class | |
3533daff |
717 | | MyApp::View::TT | instance | |
718 | '-----------------------------------------------------------------+----------' |
719 | |
720 | [debug] Loaded Private actions: |
721 | .----------------------+--------------------------------------+--------------. |
722 | | Private | Class | Method | |
723 | +----------------------+--------------------------------------+--------------+ |
724 | | /default | MyApp::Controller::Root | default | |
725 | | /end | MyApp::Controller::Root | end | |
1390ef0e |
726 | | /index | MyApp::Controller::Root | index | |
3533daff |
727 | | /books/index | MyApp::Controller::Books | index | |
728 | | /books/list | MyApp::Controller::Books | list | |
729 | '----------------------+--------------------------------------+--------------' |
730 | |
731 | [debug] Loaded Path actions: |
732 | .-------------------------------------+--------------------------------------. |
733 | | Path | Private | |
734 | +-------------------------------------+--------------------------------------+ |
1390ef0e |
735 | | / | /default | |
736 | | / | /index | |
737 | | /books | /books/index | |
3533daff |
738 | | /books/list | /books/list | |
739 | '-------------------------------------+--------------------------------------' |
740 | |
1390ef0e |
741 | [info] MyApp powered by Catalyst 5.7014 |
3533daff |
742 | You can connect to your server at http://localhost:3000 |
743 | |
1390ef0e |
744 | B<NOTE:> Be sure you run the C<script/myapp_server.pl> command from |
745 | the 'base' directory of your application, not inside the C<script> |
746 | directory itself or it will not be able to locate the C<myapp.db> |
747 | database file. You can use a fully qualified or a relative path to |
748 | locate the database file, but we did not specify that when we ran the |
3533daff |
749 | model helper earlier. |
750 | |
751 | Some things you should note in the output above: |
752 | |
753 | =over 4 |
754 | |
1390ef0e |
755 | =item * |
3533daff |
756 | |
1390ef0e |
757 | Catalyst::Model::DBIC::Schema dynamically created three model classes, |
758 | one to represent each of the three tables in our database |
d0496197 |
759 | (C<MyApp::Model::DB::Authors>, C<MyApp::Model::DB::BookAuthors>, |
760 | and C<MyApp::Model::DB::Books>). |
3533daff |
761 | |
1390ef0e |
762 | =item * |
3533daff |
763 | |
764 | The "list" action in our Books controller showed up with a path of |
765 | C</books/list>. |
766 | |
767 | =back |
768 | |
769 | Point your browser to L<http://localhost:3000> and you should still get |
770 | the Catalyst welcome page. |
771 | |
772 | Next, to view the book list, change the URL in your browser to |
773 | L<http://localhost:3000/books/list>. You should get a list of the five |
1390ef0e |
774 | books loaded by the C<myapp01.sql> script above without any formatting. |
775 | The rating for each book should appear on each row, but the "Author(s)" |
191dee29 |
776 | column will still be blank (we will fill that in later). |
3533daff |
777 | |
778 | Also notice in the output of the C<script/myapp_server.pl> that DBIC |
779 | used the following SQL to retrieve the data: |
780 | |
781 | SELECT me.id, me.title, me.rating FROM books me |
782 | |
783 | because we enabled DBIC_TRACE. |
784 | |
0c51850e |
785 | You now have the beginnings of a simple but workable web application. |
3533daff |
786 | Continue on to future sections and we will develop the application |
787 | more fully. |
788 | |
789 | |
1390ef0e |
790 | =head1 CREATE A WRAPPER FOR THE VIEW |
791 | |
792 | When using TT, you can (and should!) create a wrapper that will |
793 | literally wrap content around each of your templates. This is |
794 | certainly useful as you have one main source for changing things that |
795 | will appear across your entire site/application instead of having to |
796 | edit many individual files. |
797 | |
798 | |
799 | =head2 Configure TT.pm For The Wrapper |
800 | |
801 | In order to create a wrapper, you must first edit your TT view and |
802 | tell it where to find your wrapper file. Your TT view is located in |
803 | C<lib/MyApp/View/TT.pm>. |
804 | |
805 | Edit C<lib/MyApp/View/TT.pm> and change it to match the following: |
806 | |
807 | __PACKAGE__->config( |
808 | # Change default TT extension |
809 | TEMPLATE_EXTENSION => '.tt2', |
810 | # Set the location for TT files |
811 | INCLUDE_PATH => [ |
c2dfb562 |
812 | MyApp->path_to( 'root', 'src' ), |
1390ef0e |
813 | ], |
814 | # Set to 1 for detailed timer stats in your HTML as comments |
815 | TIMER => 0, |
816 | # This is your wrapper template located in the 'root/src' |
817 | WRAPPER => 'wrapper.tt2', |
818 | ); |
819 | |
820 | |
821 | =head2 Create the Wrapper Template File and Stylesheet |
822 | |
823 | Next you need to set up your wrapper template. Basically, you'll want |
824 | to take the overall layout of your site and put it into this file. |
825 | For the tutorial, open C<root/src/wrapper.tt2> and input the following: |
826 | |
827 | <?xml version="1.0" encoding="UTF-8"?> |
828 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
829 | <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
830 | <head> |
831 | <title>[% template.title or "My Catalyst App!" %]</title> |
832 | <link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" /> |
833 | </head> |
834 | |
835 | <body> |
836 | <div id="outer"> |
837 | <div id="header"> |
838 | [%# Your logo could go here -%] |
839 | <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" /> |
840 | [%# Insert the page title -%] |
841 | <h1>[% template.title or site.title %]</h1> |
842 | </div> |
843 | |
844 | <div id="bodyblock"> |
845 | <div id="menu"> |
846 | Navigation: |
847 | <ul> |
848 | <li><a href="[% c.uri_for('/books/list') %]">Home</a></li> |
849 | <li><a href="[% c.uri_for('/') %]" title="Catalyst Welcome Page">Welcome</a></li> |
850 | <li><a href="mailto:nobody@nowhere.com" title="Contact Us">Contact Us</a></li> |
851 | </ul> |
852 | </div><!-- end menu --> |
853 | |
854 | <div id="content"> |
855 | [%# Status and error messages %] |
856 | <span class="message">[% status_msg %]</span> |
857 | <span class="error">[% error_msg %]</span> |
858 | [%# This is where TT will stick all of your template's contents. -%] |
859 | [% content %] |
860 | </div><!-- end content --> |
861 | </div><!-- end bodyblock --> |
862 | |
863 | <div id="footer">Copyright (c) your name goes here</div> |
c2dfb562 |
864 | </div><!-- end outer --> |
1390ef0e |
865 | |
866 | </body> |
867 | </html> |
868 | |
869 | Notice the status and error message sections in the code above: |
870 | |
871 | <span class="status">[% status_msg %]</span> |
872 | <span class="error">[% error_msg %]</span> |
873 | |
874 | If we set either message in the Catalyst stash (e.g., |
875 | C<$c-E<gt>stash-E<gt>{status_msg} = 'Request was successful!'>) it |
876 | will be displayed whenever any view used by that request is rendered. |
877 | The C<message> and C<error> CSS styles can be customized to suit your |
878 | needs in the C<root/static/css/main.css> file we create below. |
879 | |
880 | B<Notes:> |
881 | |
882 | =over 4 |
883 | |
884 | =item * |
885 | |
886 | The Catalyst stash only lasts for a single HTTP request. If |
887 | you need to retain information across requests you can use |
888 | L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> (we will use |
889 | Catalyst sessions in the Authentication part of the tutorial). |
890 | |
891 | =item * |
892 | |
893 | Although it is beyond the scope of this tutorial, you may wish to use |
894 | a JavaScript or AJAX tool such as jQuery (L<http://www.jquery.com>) or |
895 | Dojo (L<http://www.dojotoolkit.org>). |
896 | |
897 | =back |
898 | |
899 | |
900 | =head3 Create A Basic Stylesheet |
901 | |
902 | First create a central location for stylesheets under the static |
903 | directory: |
904 | |
905 | $ mkdir root/static/css |
906 | |
907 | Then open the file C<root/static/css/main.css> (the file referenced in |
908 | the stylesheet href link of our wrapper above) and add the following |
909 | content: |
910 | |
911 | #header { |
912 | text-align: center; |
913 | } |
914 | #header h1 { |
915 | margin: 0; |
916 | } |
917 | #header img { |
918 | float: right; |
919 | } |
920 | #footer { |
921 | text-align: center; |
922 | font-style: italic; |
923 | padding-top: 20px; |
924 | } |
925 | #menu { |
926 | font-weight: bold; |
927 | background-color: #ddd; |
928 | } |
929 | #menu ul { |
930 | list-style: none; |
931 | float: left; |
932 | margin: 0; |
933 | padding: 0 0 50% 5px; |
934 | font-weight: normal; |
935 | background-color: #ddd; |
936 | width: 100px; |
937 | } |
938 | #content { |
939 | margin-left: 120px; |
940 | } |
941 | .message { |
942 | color: #390; |
943 | } |
944 | .error { |
945 | color: #f00; |
946 | } |
947 | |
948 | You may wish to check out a "CSS Framework" like Emastic |
949 | (L<http://code.google.com/p/emastic/>) as a way to quickly |
950 | provide lots of high-quality CSS functionality. |
951 | |
952 | |
953 | =head2 Test Run The Application |
954 | |
955 | Restart the development server and hit "Reload" in your web browser |
956 | and you should now see a formatted version of our basic book list. |
957 | Although our wrapper and stylesheet are obviously very simple, you |
958 | should see how it allows us to control the overall look of an entire |
959 | website from two central files. To add new pages to the site, just |
960 | provide a template that fills in the C<content> section of our wrapper |
961 | template -- the wrapper will provide the overall feel of the page. |
962 | |
963 | |
3533daff |
964 | =head1 A STATIC DATABASE MODEL WITH C<DBIx::Class> |
965 | |
966 | =head2 Create Static DBIC Schema Files |
967 | |
1390ef0e |
968 | Unlike the previous DBIC section where we had C<create=dynamic> |
969 | automatically discover the structure of the database every time the |
970 | application started, here we will use static schema files for more |
971 | control. This is typical of most "real world" applications. |
3533daff |
972 | |
1390ef0e |
973 | One option would be to manually create a separate schema file for each |
974 | table in the database, however, lets use the same |
975 | L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> used |
976 | earlier with C<create=dynamic> to build the static files for us. |
9ad715b3 |
977 | First, lets remove the schema file created earlier: |
3533daff |
978 | |
1390ef0e |
979 | $ rm lib/MyApp/Schema.pm |
3533daff |
980 | |
981 | Now regenerate the schema using the C<create=static> option: |
982 | |
d0496197 |
983 | $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema create=static dbi:SQLite:myapp.db |
984 | exists "/home/kclark/dev/MyApp/script/../lib/MyApp/Model" |
985 | exists "/home/kclark/dev/MyApp/script/../t" |
986 | Dumping manual schema for MyApp::Schema to directory /home/kclark/dev/MyApp/script/../lib ... |
3533daff |
987 | Schema dump completed. |
d0496197 |
988 | exists "/home/kclark/dev/MyApp/script/../lib/MyApp/Model/DB.pm" |
3533daff |
989 | |
1390ef0e |
990 | We could have also deleted C<lib/MyApp/Model/DB.pm>, but it would |
3533daff |
991 | have regenerated the same file (note the C<exists> in the output above). |
d0496197 |
992 | If you take a look at C<lib/MyApp/Model/DB.pm>, it simply contains |
993 | a reference to the actual schema file in C<lib/MyApp/Schema.pm> |
3533daff |
994 | along with the database connect string. |
995 | |
1390ef0e |
996 | If you look in the C<lib/MyApp/Schema.pm> file, you will find that it |
997 | is no longer using |
998 | L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> as its base |
999 | class (L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> is |
1000 | only being used by the helper to load the schema once and then create |
1001 | the static files for us) and C<Schema.pm> only contains a call to the |
c2dfb562 |
1002 | C<load_classes> method. You will also find that C<lib/MyApp> |
1390ef0e |
1003 | contains a C<Schema> subdirectory, with one file inside this directory |
1004 | for each of the tables in our simple database (C<Authors.pm>, |
1005 | C<BookAuthors.pm>, and C<Books.pm>). These three files were created |
1006 | based on the information found by |
1007 | L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> as the |
1008 | helper ran. |
1009 | |
1010 | The idea with all of the files created under C<lib/MyApp/Schema> by |
1011 | the C<create=static> option is to only edit the files below the C<# DO |
1012 | NOT MODIFY THIS OR ANYTHING ABOVE!> warning. If you place all of your |
3533daff |
1013 | changes below that point in the file, you can regenerate the |
1390ef0e |
1014 | automatically created information at the top of each file should your |
1015 | database structure get updated. |
3533daff |
1016 | |
1390ef0e |
1017 | Also note the "flow" of the model information across the various files |
1018 | and directories. Catalyst will initially load the model from |
d0496197 |
1019 | C<lib/MyApp/Model/DB.pm>. This file contains a reference to |
1020 | C<lib/MyApp/Schema.pm>, so that file is loaded next. Finally, |
1390ef0e |
1021 | the call to C<load_classes> in C<Schema.pm> will load each of the |
d0496197 |
1022 | table-specific "results source" files from the C<lib/MyApp/Schema> |
1390ef0e |
1023 | subdirectory. These three table-specific DBIC schema files will then be |
1024 | used to create three table-specific Catalyst models every time the |
3533daff |
1025 | application starts (you can see these three model files listed in |
1026 | the debug output generated when you launch the application). |
1027 | |
1028 | |
1029 | =head2 Updating the Generated DBIC Schema Files |
1030 | |
3533daff |
1031 | Let's manually add some relationship information to the auto-generated |
d0496197 |
1032 | schema files. First edit C<lib/MyApp/Schema/Books.pm> and |
1390ef0e |
1033 | add the following text below the C<# You can replace this text...> |
3533daff |
1034 | comment: |
1035 | |
1036 | # |
1037 | # Set relationships: |
1390ef0e |
1038 | # |
3533daff |
1039 | |
1040 | # has_many(): |
1041 | # args: |
1042 | # 1) Name of relationship, DBIC will create accessor with this name |
1043 | # 2) Name of the model class referenced by this relationship |
1044 | # 3) Column name in *foreign* table |
d0496197 |
1045 | __PACKAGE__->has_many(book_authors => 'MyApp::Schema::BookAuthors', 'book_id'); |
3533daff |
1046 | |
1047 | # many_to_many(): |
1048 | # args: |
1049 | # 1) Name of relationship, DBIC will create accessor with this name |
1390ef0e |
1050 | # 2) Name of has_many() relationship this many_to_many() is shortcut for |
1051 | # 3) Name of belongs_to() relationship in model class of has_many() above |
3533daff |
1052 | # You must already have the has_many() defined to use a many_to_many(). |
1053 | __PACKAGE__->many_to_many(authors => 'book_authors', 'author'); |
1054 | |
1055 | |
1056 | B<Note:> Be careful to put this code I<above> the C<1;> at the end of the |
1057 | file. As with any Perl package, we need to end the last line with |
1058 | a statement that evaluates to C<true>. This is customarily done with |
1059 | C<1;> on a line by itself. |
1060 | |
1390ef0e |
1061 | This code defines both a C<has_many> and a C<many_to_many> relationship. |
1062 | The C<many_to_many> relationship is optional, but it makes it easier to |
1063 | map a book to its collection of authors. Without it, we would have to |
1064 | "walk" though the C<book_authors> table as in |
1065 | C<$book-E<gt>book_authors-E<gt>first-E<gt>author-E<gt>last_name> |
1066 | (we will see examples on how to use DBIC objects in your code soon, |
1067 | but note that because C<$book-E<gt>book_authors> can return multiple |
1068 | authors, we have to use C<first> to display a single author). |
1069 | C<many_to_many> allows us to use the shorter |
1070 | C<$book-E<gt>authors-E<gt>first-E<gt>last_name>. |
1071 | Note that you cannot define a C<many_to_many> relationship without |
1072 | also having the C<has_many> relationship in place. |
3533daff |
1073 | |
d0496197 |
1074 | Then edit C<lib/MyApp/Schema/Authors.pm> and add relationship |
3533daff |
1075 | information as follows (again, be careful to put in above the C<1;> but |
1076 | below the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment): |
1077 | |
1078 | # |
1079 | # Set relationships: |
1080 | # |
1081 | |
1082 | # has_many(): |
1083 | # args: |
1084 | # 1) Name of relationship, DBIC will create accessor with this name |
1085 | # 2) Name of the model class referenced by this relationship |
1086 | # 3) Column name in *foreign* table |
d0496197 |
1087 | __PACKAGE__->has_many(book_author => 'MyApp::Schema::BookAuthors', 'author_id'); |
3533daff |
1088 | |
1089 | # many_to_many(): |
1090 | # args: |
1091 | # 1) Name of relationship, DBIC will create accessor with this name |
1092 | # 2) Name of has_many() relationship this many_to_many() is shortcut for |
1390ef0e |
1093 | # 3) Name of belongs_to() relationship in model class of has_many() above |
3533daff |
1094 | # You must already have the has_many() defined to use a many_to_many(). |
1095 | __PACKAGE__->many_to_many(books => 'book_author', 'book'); |
1096 | |
1390ef0e |
1097 | Finally, do the same for the "join table," |
d0496197 |
1098 | C<lib/MyApp/Schema/BookAuthors.pm>: |
3533daff |
1099 | |
1100 | # |
1101 | # Set relationships: |
1102 | # |
1103 | |
1104 | # belongs_to(): |
1105 | # args: |
1106 | # 1) Name of relationship, DBIC will create accessor with this name |
1107 | # 2) Name of the model class referenced by this relationship |
1108 | # 3) Column name in *this* table |
d0496197 |
1109 | __PACKAGE__->belongs_to(book => 'MyApp::Schema::Books', 'book_id'); |
3533daff |
1110 | |
1111 | # belongs_to(): |
1112 | # args: |
1113 | # 1) Name of relationship, DBIC will create accessor with this name |
1114 | # 2) Name of the model class referenced by this relationship |
1115 | # 3) Column name in *this* table |
d0496197 |
1116 | __PACKAGE__->belongs_to(author => 'MyApp::Schema::Authors', 'author_id'); |
3533daff |
1117 | |
1118 | |
1390ef0e |
1119 | =head2 Run The Application |
3533daff |
1120 | |
1121 | Run the Catalyst "demo server" script with the C<DBIC_TRACE> option |
1122 | (it might still be enabled from earlier in the tutorial, but here |
1123 | is an alternate way to specify the option just in case): |
1124 | |
1125 | $ DBIC_TRACE=1 script/myapp_server.pl |
1126 | |
1390ef0e |
1127 | Make sure that the application loads correctly and that you see the |
1128 | three dynamically created model class (one for each of the |
3533daff |
1129 | table-specific schema classes we created). |
1130 | |
c2dfb562 |
1131 | Then hit the URL L<http://localhost:3000/books/list> and be sure that |
1132 | the book list is displayed via the relationships established above. You |
1133 | can leave the development server running for the next step if you wish. |
3533daff |
1134 | |
c2dfb562 |
1135 | B<Note:> You will not see the authors yet because the view does not yet |
1136 | use the new relations. Read on to the next section where we update the |
1137 | template to do that. |
3533daff |
1138 | |
1139 | |
1140 | =head1 UPDATING THE VIEW |
1141 | |
1142 | Let's add a new column to our book list page that takes advantage of |
1143 | the relationship information we manually added to our schema files |
1144 | in the previous section. Edit C<root/src/books/list.tt2> add add the |
1145 | following code below the existing table cell that contains |
1390ef0e |
1146 | C<book.rating> (IOW, add a new table cell below the existing two |
3533daff |
1147 | C<td> cells): |
1148 | |
1149 | <td> |
1150 | [% # First initialize a TT variable to hold a list. Then use a TT FOREACH -%] |
1151 | [% # loop in 'side effect notation' to load just the last names of the -%] |
a0c5188a |
1152 | [% # authors into the list. Note that the 'push' TT vmethod does not print -%] |
3533daff |
1153 | [% # a value, so nothing will be printed here. But, if you have something -%] |
1154 | [% # in TT that does return a method and you don't want it printed, you -%] |
1155 | [% # can: 1) assign it to a bogus value, or 2) use the CALL keyword to -%] |
1156 | [% # call it and discard the return value. -%] |
1157 | [% tt_authors = [ ]; |
1158 | tt_authors.push(author.last_name) FOREACH author = book.authors %] |
1159 | [% # Now use a TT 'virtual method' to display the author count in parens -%] |
1160 | [% # Note the use of the TT filter "| html" to escape dangerous characters -%] |
1161 | ([% tt_authors.size | html %]) |
1162 | [% # Use another TT vmethod to join & print the names & comma separators -%] |
1163 | [% tt_authors.join(', ') | html %] |
1164 | </td> |
1165 | |
1390ef0e |
1166 | Then hit "Reload" in your browser (note that you don't need to reload |
3533daff |
1167 | the development server or use the C<-r> option when updating TT |
1390ef0e |
1168 | templates) and you should now see the number of authors each book has |
1169 | along with a comma-separated list of the authors' last names. (If you |
1170 | didn't leave the development server running from the previous step, |
1171 | you will obviously need to start it before you can refresh your |
1172 | browser window.) |
1173 | |
1174 | If you are still running the development server with C<DBIC_TRACE> |
1175 | enabled, you should also now see five more C<SELECT> statements in the |
1176 | debug output (one for each book as the authors are being retrieved by |
3533daff |
1177 | DBIC). |
1178 | |
c2dfb562 |
1179 | SELECT me.id, me.title, me.rating FROM books me: |
1180 | SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '1' |
1181 | SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '2' |
1182 | SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '3' |
1183 | SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '4' |
1184 | SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '5' |
1185 | |
1186 | Also note in C<root/src/books/list.tt2> that we are using "| html", a |
1187 | type of TT filter, to escape characters such as E<lt> and E<gt> to < |
1188 | and > and avoid various types of dangerous hacks against your |
1189 | application. In a real application, you would probably want to put |
1190 | "| html" at the end of every field where a user has control over the |
1191 | information that can appear in that field (and can therefore inject |
1192 | markup or code if you don't "neutralize" those fields). In addition to |
1193 | "| html", Template Toolkit has a variety of other useful filters that |
1194 | can found in the documentation for |
1195 | L<Template::Filters|Template::Filters>. |
3533daff |
1196 | |
1197 | |
1390ef0e |
1198 | =head1 RUNNING THE APPLICATION FROM THE COMMAND LINE |
1199 | |
1200 | In some situations, it can be useful to run your application and |
1201 | display a page without using a browser. Catalyst lets you do this |
1202 | using the C<scripts/myapp_test.pl> script. Just supply the URL you |
1203 | wish to display and it will run that request through the normal |
1204 | controller dispatch logic and use the appropriate view to render the |
1205 | output (obviously, complex pages may dump a lot of text to your |
1206 | terminal window). For example, if you type: |
1207 | |
1208 | $ script/myapp_test.pl "/books/list" |
1209 | |
1210 | You should get the same text as if you visited |
1211 | L<http://localhost:3000/books/list> with the normal development server |
1212 | and asked your browser to view the page source. |
3533daff |
1213 | |
1390ef0e |
1214 | |
1215 | =head1 OPTIONAL INFORMATION |
1216 | |
1217 | B<NOTE: The rest of this part of the tutorial is optional. You can |
1218 | skip to Part 4, L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>, |
3533daff |
1219 | if you wish.> |
1220 | |
1390ef0e |
1221 | =head2 Using C<RenderView> for the Default View |
1222 | |
1223 | Once your controller logic has processed the request from a user, it |
1224 | forwards processing to your view in order to generate the appropriate |
3533daff |
1225 | response output. Catalyst uses |
1390ef0e |
1226 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> by |
1227 | default to automatically performs this operation. If you look in |
1228 | C<lib/MyApp/Controller/Root.pm>, you should see the empty |
3533daff |
1229 | definition for the C<sub end> method: |
1230 | |
1231 | sub end : ActionClass('RenderView') {} |
1232 | |
1390ef0e |
1233 | The following bullet points provide a quick overview of the |
3533daff |
1234 | C<RenderView> process: |
1235 | |
1236 | =over 4 |
1237 | |
1238 | =item * |
1239 | |
1240 | C<Root.pm> is designed to hold application-wide logic. |
1241 | |
1242 | =item * |
1243 | |
1390ef0e |
1244 | At the end of a given user request, Catalyst will call the most specific |
1245 | C<end> method that's appropriate. For example, if the controller for a |
1246 | request has an C<end> method defined, it will be called. However, if |
1247 | the controller does not define a controller-specific C<end> method, the |
3533daff |
1248 | "global" C<end> method in C<Root.pm> will be called. |
1249 | |
1250 | =item * |
1251 | |
1252 | Because the definition includes an C<ActionClass> attribute, the |
1253 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> logic |
1254 | will be executed B<after> any code inside the definition of C<sub end> |
1255 | is run. See L<Catalyst::Manual::Actions|Catalyst::Manual::Actions> |
1256 | for more information on C<ActionClass>. |
1257 | |
1258 | =item * |
1259 | |
1390ef0e |
1260 | Because C<sub end> is empty, this effectively just runs the default |
1261 | logic in C<RenderView>. However, you can easily extend the |
1262 | C<RenderView> logic by adding your own code inside the empty method body |
1263 | (C<{}>) created by the Catalyst Helpers when we first ran the |
1264 | C<catalyst.pl> to initialize our application. See |
1265 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> for more |
3533daff |
1266 | detailed information on how to extended C<RenderView> in C<sub end>. |
1267 | |
1268 | =back |
1269 | |
1270 | |
1271 | =head2 Using The Default Template Name |
1272 | |
1390ef0e |
1273 | By default, C<Catalyst::View::TT> will look for a template that uses the |
1274 | same name as your controller action, allowing you to save the step of |
1275 | manually specifying the template name in each action. For example, this |
1276 | would allow us to remove the |
1277 | C<$c-E<gt>stash-E<gt>{template} = 'books/list.tt2';> line of our |
1278 | C<list> action in the Books controller. Open |
3533daff |
1279 | C<lib/MyApp/Controller/Books.pm> in your editor and comment out this line |
1280 | to match the following (only the C<$c-E<gt>stash-E<gt>{template}> line |
1281 | has changed): |
1282 | |
1283 | =head2 list |
1284 | |
1285 | Fetch all book objects and pass to books/list.tt2 in stash to be displayed |
1286 | |
1287 | =cut |
1288 | |
1289 | sub list : Local { |
1290 | # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst |
1291 | # 'Context' that's used to 'glue together' the various components |
1292 | # that make up the application |
1293 | my ($self, $c) = @_; |
1294 | |
1295 | # Retrieve all of the book records as book model objects and store in the |
1296 | # stash where they can be accessed by the TT template |
d0496197 |
1297 | $c->stash->{books} = [$c->model('DB::Books')->all]; |
3533daff |
1298 | |
1299 | # Set the TT template to use. You will almost always want to do this |
1300 | # in your action methods (actions methods respond to user input in |
1301 | # your controllers). |
1302 | #$c->stash->{template} = 'books/list.tt2'; |
1303 | } |
1304 | |
3533daff |
1305 | |
1390ef0e |
1306 | You should now be able to restart the development server as per the |
3533daff |
1307 | previous section and access the L<http://localhost:3000/books/list> |
1308 | as before. |
1309 | |
1310 | B<NOTE:> Please note that if you use the default template technique, |
1311 | you will B<not> be able to use either the C<$c-E<gt>forward> or |
1390ef0e |
1312 | the C<$c-E<gt>detach> mechanisms (these are discussed in Part 2 and |
3533daff |
1313 | Part 9 of the Tutorial). |
1314 | |
1315 | |
1316 | =head2 Return To A Manually-Specified Template |
1317 | |
1318 | In order to be able to use C<$c-E<gt>forward> and C<$c-E<gt>detach> |
1319 | later in the tutorial, you should remove the comment from the |
1320 | statement in C<sub list> in C<lib/MyApp/Controller/Books.pm>: |
1321 | |
1322 | $c->stash->{template} = 'books/list.tt2'; |
1323 | |
1390ef0e |
1324 | Then delete the C<TEMPLATE_EXTENSION> line in |
3533daff |
1325 | C<lib/MyApp/View/TT.pm>. |
1326 | |
1390ef0e |
1327 | You should then be able to restart the development server and |
3533daff |
1328 | access L<http://localhost:3000/books/list> in the same manner as |
1329 | with earlier sections. |
1330 | |
1331 | |
1332 | =head1 AUTHOR |
1333 | |
1334 | Kennedy Clark, C<hkclark@gmail.com> |
1335 | |
1336 | Please report any errors, issues or suggestions to the author. The |
1337 | most recent version of the Catalyst Tutorial can be found at |
82ab4bbf |
1338 | L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.70/trunk/lib/Catalyst/Manual/Tutorial/>. |
3533daff |
1339 | |
45c7830f |
1340 | Copyright 2006-2008, Kennedy Clark, under Creative Commons License |
8482d557 |
1341 | (L<http://creativecommons.org/licenses/by-sa/3.0/us/>). |