Commit | Line | Data |
4d583dd8 |
1 | =head1 NAME |
2 | |
64ccd8a8 |
3 | Catalyst::Manual::Tutorial::CatalystBasics - Catalyst Tutorial - Part 2: Catalyst Application Development Basics |
4d583dd8 |
4 | |
5 | |
4d583dd8 |
6 | =head1 OVERVIEW |
7 | |
8 | This is B<Part 2 of 9> for the Catalyst tutorial. |
9 | |
64ccd8a8 |
10 | L<Tutorial Overview|Catalyst::Manual::Tutorial> |
4d583dd8 |
11 | |
12 | =over 4 |
13 | |
14 | =item 1 |
15 | |
16 | L<Introduction|Catalyst::Manual::Tutorial::Intro> |
17 | |
18 | =item 2 |
19 | |
20 | B<Catalyst Basics> |
21 | |
22 | =item 3 |
23 | |
653f4595 |
24 | L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD> |
4d583dd8 |
25 | |
26 | =item 4 |
27 | |
28 | L<Authentication|Catalyst::Manual::Tutorial::Authentication> |
29 | |
30 | =item 5 |
31 | |
32 | L<Authorization|Catalyst::Manual::Tutorial::Authorization> |
33 | |
34 | =item 6 |
35 | |
36 | L<Debugging|Catalyst::Manual::Tutorial::Debugging> |
37 | |
38 | =item 7 |
39 | |
40 | L<Testing|Catalyst::Manual::Tutorial::Testing> |
41 | |
42 | =item 8 |
43 | |
653f4595 |
44 | L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD> |
4d583dd8 |
45 | |
46 | =item 9 |
47 | |
7d310f12 |
48 | L<Appendices|Catalyst::Manual::Tutorial::Appendices> |
4d583dd8 |
49 | |
50 | =back |
51 | |
208513f1 |
52 | |
4d583dd8 |
53 | =head1 DESCRIPTION |
54 | |
64ccd8a8 |
55 | In this part of the tutorial, we will create a very basic Catalyst web |
56 | application. Though simple in many respects, this section will already |
57 | demonstrate a number of powerful capabilities such as: |
4d583dd8 |
58 | |
59 | =over 4 |
60 | |
61 | =item * Helper Scripts |
62 | |
64ccd8a8 |
63 | Catalyst helper scripts that can be used to rapidly bootstrap the |
64 | skeletal structure of an application. |
4d583dd8 |
65 | |
66 | =item * MVC |
67 | |
64ccd8a8 |
68 | Model/View/Controller (MVC) provides an architecture that facilitates a |
69 | clean "separation of control" between the different portions of your |
653f4595 |
70 | application. Given that many other documents cover this subject in |
64ccd8a8 |
71 | detail, MVC will not be discussed in depth here (for an excellent |
72 | introduction to MVC and general Catalyst concepts, please see |
653f4595 |
73 | L<Catalyst::Manual::About>. In short: |
4d583dd8 |
74 | |
75 | =over 4 |
76 | |
77 | =item * Model |
78 | |
653f4595 |
79 | The model usually represents a data store. In most applications, the |
80 | model equates to the objects that are created from and saved to your SQL |
81 | database. |
4d583dd8 |
82 | |
83 | =item * View |
84 | |
64ccd8a8 |
85 | The view takes model objects and renders them into something for the end |
653f4595 |
86 | user to look at. Normally this involves a template-generation tool that |
64ccd8a8 |
87 | creates HTML for the user's web browser, but it could easily be code |
653f4595 |
88 | that generates other forms such as PDF documents, e-mails, or Excel |
89 | spreadsheets. |
4d583dd8 |
90 | |
91 | =item * Controller |
92 | |
64ccd8a8 |
93 | As suggested by its name, the controller takes user requests and routes |
94 | them to the necessary model and view. |
4d583dd8 |
95 | |
96 | =back |
97 | |
98 | =item * ORM |
99 | |
653f4595 |
100 | The use of Object-Relational Mapping (ORM) technology for database |
101 | access. Specifically, ORM provides an automated and standardized means |
102 | to persist and restore objects to/from a relational database. |
4d583dd8 |
103 | |
104 | =back |
105 | |
64ccd8a8 |
106 | B<TIP>: Note that all of the code for this part of the tutorial can be |
107 | pulled from the Catalyst Subversion repository in one step with the |
108 | following command: |
4d583dd8 |
109 | |
7d310f12 |
110 | svn co http://dev.catalyst.perl.org/repos/Catalyst/tags/examples/Tutorial/MyApp/5.7/CatalystBasics MyApp |
4d583dd8 |
111 | |
112 | |
4d583dd8 |
113 | =head1 CREATE A CATALYST PROJECT |
114 | |
64ccd8a8 |
115 | Catalyst provides a number of helper scripts that can be used to quickly |
653f4595 |
116 | flesh out the basic structure of your application. All Catalyst projects |
8112f931 |
117 | begin with the C<catalyst.pl> helper (see L<Catalyst::Helper|Catalyst::Helper> |
118 | for more information on helpers). Also note that as of Catalyst 5.7000, |
119 | you will not have the helper scripts unless you install both |
120 | L<Catalyst::Runtime|Catalyst::Runtime> and L<Catalyst::Devel|Catalyst::Devel>. |
4d583dd8 |
121 | |
64ccd8a8 |
122 | In the case of this tutorial, use the Catalyst C<catalyst.pl> script to |
123 | initialize the framework for an application called C<MyApp>: |
4d583dd8 |
124 | |
125 | $ catalyst.pl MyApp |
208513f1 |
126 | created "MyApp" |
127 | created "MyApp/script" |
128 | created "MyApp/lib" |
129 | created "MyApp/root" |
130 | ... |
131 | created "MyApp/script/myapp_create.pl" |
4d583dd8 |
132 | $ cd MyApp |
133 | |
64ccd8a8 |
134 | The C<catalyst.pl> helper script will display the names of the |
135 | directories and files it creates. |
4d583dd8 |
136 | |
653f4595 |
137 | Though it's too early for any significant celebration, we already have a |
138 | functioning application. Run the following command to run this |
139 | application with the built-in development web server: |
4d583dd8 |
140 | |
208513f1 |
141 | $ script/myapp_server.pl |
142 | [debug] Debug messages enabled |
143 | [debug] Loaded plugins: |
144 | .----------------------------------------------------------------------------. |
145 | | Catalyst::Plugin::ConfigLoader 0.06 | |
146 | | Catalyst::Plugin::Static::Simple 0.14 | |
147 | '----------------------------------------------------------------------------' |
148 | |
149 | [debug] Loaded dispatcher "Catalyst::Dispatcher" |
150 | [debug] Loaded engine "Catalyst::Engine::HTTP" |
151 | [debug] Found home "/root/dev/MyApp" |
152 | [debug] Loaded components: |
153 | .-----------------------------------------------------------------+----------. |
154 | | Class | Type | |
155 | +-----------------------------------------------------------------+----------+ |
156 | | MyApp::Controller::Root | instance | |
157 | '-----------------------------------------------------------------+----------' |
158 | |
159 | [debug] Loaded Private actions: |
160 | .----------------------+--------------------------------------+--------------. |
161 | | Private | Class | Method | |
162 | +----------------------+--------------------------------------+--------------+ |
163 | | /default | MyApp::Controller::Root | default | |
164 | | /end | MyApp::Controller::Root | end | |
165 | '----------------------+--------------------------------------+--------------' |
166 | |
8e956464 |
167 | [info] MyApp powered by Catalyst 5.7000 |
208513f1 |
168 | You can connect to your server at http://localhost.localdomain:3000 |
169 | |
f5ca9e89 |
170 | B<NOTE>: Be sure you run the C<script/myapp_server.pl> command from the |
171 | 'base' directory of your application, not inside the C<script> directory |
172 | itself. It doesn't make a difference at this point, but it will as soon |
173 | as we get the database going in the next section. |
174 | |
208513f1 |
175 | Point your web browser to L<http://localhost:3000> (substituting a |
176 | different hostname or IP address as appropriate) and you should be |
177 | greeted by the Catalyst welcome screen. Information similar to the |
178 | following should be appended to the logging output of the development |
179 | server: |
180 | |
7d310f12 |
181 | [info] *** Request 1 (0.043/s) [6003] [Fri Jul 7 13:32:53 2006] *** |
208513f1 |
182 | [debug] "GET" request for "/" from "127.0.0.1" |
7d310f12 |
183 | [info] Request took 0.067675s (14.777/s) |
208513f1 |
184 | .----------------------------------------------------------------+-----------. |
185 | | Action | Time | |
186 | +----------------------------------------------------------------+-----------+ |
7d310f12 |
187 | | /default | 0.002844s | |
188 | | /end | 0.000207s | |
208513f1 |
189 | '----------------------------------------------------------------+-----------' |
190 | |
191 | Press Ctrl-C to break out of the development server. |
4d583dd8 |
192 | |
4d583dd8 |
193 | |
4d583dd8 |
194 | =head1 CREATE A SQLITE DATABASE |
195 | |
64ccd8a8 |
196 | In this step, we make a text file with the required SQL commands to |
197 | create a database table and load some sample data. Open C<myapp01.sql> |
198 | in your editor and enter: |
4d583dd8 |
199 | |
200 | -- |
201 | -- Create a very simple database to hold book and author information |
202 | -- |
203 | CREATE TABLE books ( |
204 | id INTEGER PRIMARY KEY, |
205 | title TEXT , |
206 | rating INTEGER |
207 | ); |
208 | -- 'book_authors' is a many-to-many join table between books & authors |
209 | CREATE TABLE book_authors ( |
210 | book_id INTEGER, |
211 | author_id INTEGER, |
212 | PRIMARY KEY (book_id, author_id) |
213 | ); |
214 | CREATE TABLE authors ( |
215 | id INTEGER PRIMARY KEY, |
216 | first_name TEXT, |
217 | last_name TEXT |
218 | ); |
219 | --- |
220 | --- Load some sample data |
221 | --- |
222 | INSERT INTO books VALUES (1, 'CCSP SNRS Exam Certification Guide', 5); |
223 | INSERT INTO books VALUES (2, 'TCP/IP Illustrated, Volume 1', 5); |
224 | INSERT INTO books VALUES (3, 'Internetworking with TCP/IP Vol.1', 4); |
225 | INSERT INTO books VALUES (4, 'Perl Cookbook', 5); |
226 | INSERT INTO books VALUES (5, 'Designing with Web Standards', 5); |
227 | INSERT INTO authors VALUES (1, 'Greg', 'Bastien'); |
228 | INSERT INTO authors VALUES (2, 'Sara', 'Nasseh'); |
229 | INSERT INTO authors VALUES (3, 'Christian', 'Degu'); |
230 | INSERT INTO authors VALUES (4, 'Richard', 'Stevens'); |
231 | INSERT INTO authors VALUES (5, 'Douglas', 'Comer'); |
232 | INSERT INTO authors VALUES (6, 'Tom', 'Christiansen'); |
233 | INSERT INTO authors VALUES (7, ' Nathan', 'Torkington'); |
234 | INSERT INTO authors VALUES (8, 'Jeffrey', 'Zeldman'); |
235 | INSERT INTO book_authors VALUES (1, 1); |
236 | INSERT INTO book_authors VALUES (1, 2); |
237 | INSERT INTO book_authors VALUES (1, 3); |
238 | INSERT INTO book_authors VALUES (2, 4); |
239 | INSERT INTO book_authors VALUES (3, 5); |
240 | INSERT INTO book_authors VALUES (4, 6); |
241 | INSERT INTO book_authors VALUES (4, 7); |
242 | INSERT INTO book_authors VALUES (5, 8); |
243 | |
64ccd8a8 |
244 | B<TIP>: See Appendix 1 for tips on removing the leading spaces when |
c608fae5 |
245 | cutting and pasting example code from POD-based documents. |
4d583dd8 |
246 | |
247 | Then use the following command to build a C<myapp.db> SQLite database: |
248 | |
249 | $ sqlite3 myapp.db < myapp01.sql |
250 | |
64ccd8a8 |
251 | If you need to create the database more than once, you probably want to |
252 | issue the C<rm myapp.db> command to delete the database before you use |
253 | the C<sqlite3 myapp.db < myapp01.sql> command. |
4d583dd8 |
254 | |
64ccd8a8 |
255 | Once the C<myapp.db> database file has been created and initialized, you |
256 | can use the SQLite command line environment to do a quick dump of the |
257 | database contents: |
4d583dd8 |
258 | |
259 | $ sqlite3 myapp.db |
260 | SQLite version 3.2.2 |
261 | Enter ".help" for instructions |
262 | sqlite> select * from books; |
263 | 1|CCSP SNRS Exam Certification Guide|5 |
264 | 2|TCP/IP Illustrated, Volume 1|5 |
265 | 3|Internetworking with TCP/IP Vol.1|4 |
266 | 4|Perl Cookbook|5 |
267 | 5|Designing with Web Standards|5 |
268 | sqlite> .q |
269 | $ |
270 | |
271 | Or: |
272 | |
273 | $ sqlite3 myapp.db "select * from books" |
274 | 1|CCSP SNRS Exam Certification Guide|5 |
275 | 2|TCP/IP Illustrated, Volume 1|5 |
276 | 3|Internetworking with TCP/IP Vol.1|4 |
277 | 4|Perl Cookbook|5 |
278 | 5|Designing with Web Standards|5 |
279 | |
64ccd8a8 |
280 | As with most other SQL tools, if you are using the full "interactive" |
281 | environment you need to terminate your SQL commands with a ";" (it's not |
282 | required if you do a single SQL statement on the command line). Use |
283 | ".q" to exit from SQLite from the SQLite interactive mode and return to |
284 | your OS command prompt. |
4d583dd8 |
285 | |
286 | |
4d583dd8 |
287 | =head1 EDIT THE LIST OF CATALYST PLUGINS |
288 | |
64ccd8a8 |
289 | One of the greatest benefits of Catalyst is that it has such a large |
290 | library of plugins available. Plugins are used to seamlessly integrate |
291 | existing Perl modules into the overall Catalyst framework. In general, |
292 | they do this by adding additional methods to the C<context> object |
293 | (generally written as C<$c>) that Catalyst passes to every component |
294 | throughout the framework. |
4d583dd8 |
295 | |
296 | By default, Catalyst enables three plugins/flags: |
297 | |
298 | =over 4 |
299 | |
300 | =item * |
301 | |
302 | C<-Debug> Flag |
303 | |
64ccd8a8 |
304 | Enables the Catalyst debug output you saw when we started the |
305 | C<script/myapp_server.pl> development server earlier. You can remove |
306 | this plugin when you place your application into production. |
4d583dd8 |
307 | |
64ccd8a8 |
308 | As you may have noticed, C<-Debug> is not a plugin, but a I<flag>. |
309 | Although most of the items specified on the C<use Catalyst> line of your |
310 | application class will be plugins, Catalyst supports a limited number of |
14e6feb0 |
311 | flag options (of these, C<-Debug> is the most common). See the |
312 | documentation for C<Catalyst.pm> to get details on other flags |
313 | (currently C<-Engine>, C<-Home>, and C<-Log>). |
4d583dd8 |
314 | |
64ccd8a8 |
315 | If you prefer, you can use the C<$c-E<gt>debug> method to enable debug |
316 | messages. |
4d583dd8 |
317 | |
318 | =item * |
319 | |
320 | L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> |
321 | |
653f4595 |
322 | C<ConfigLoader> provides an automatic way to load configurable |
64ccd8a8 |
323 | parameters for your application from a central YAML file (versus having |
324 | the values hard-coded inside your Perl modules). If you have not been |
325 | exposed to YAML before, it is a human-readable data serialization format |
326 | that can be used to read (and write) values to/from text files. We will |
327 | see how to use this feature of Catalyst during the authentication and |
328 | authorization sections (Part 4 and Part 5). |
4d583dd8 |
329 | |
4d583dd8 |
330 | =item * |
331 | |
14e6feb0 |
332 | L<Catalyst::Plugin::Static::Simple|Catalyst::Plugin::Static::Simple> |
4d583dd8 |
333 | |
64ccd8a8 |
334 | C<Static::Simple> provides an easy method of serving static content such |
335 | as images and CSS files under the development server. |
4d583dd8 |
336 | |
337 | =back |
338 | |
64ccd8a8 |
339 | To modify the list of plugins, edit C<lib/MyApp.pm> (this file is |
340 | generally referred to as your I<application class>) and delete the line |
341 | with: |
4d583dd8 |
342 | |
343 | use Catalyst qw/-Debug ConfigLoader Static::Simple/; |
344 | |
345 | Replace it with: |
346 | |
347 | use Catalyst qw/ |
348 | -Debug |
349 | ConfigLoader |
350 | Static::Simple |
351 | |
4d583dd8 |
352 | StackTrace |
4d583dd8 |
353 | /; |
354 | |
c608fae5 |
355 | This tells Catalyst to start using one new plugin: |
4d583dd8 |
356 | |
357 | =over 4 |
358 | |
359 | =item * |
360 | |
4d583dd8 |
361 | L<Catalyst::Plugin::StackTrace|Catalyst::Plugin::StackTrace> |
362 | |
64ccd8a8 |
363 | Adds a stack trace to the standard Catalyst "debug screen" (this is the |
364 | screen Catalyst sends to your browser when an error occurs). |
4d583dd8 |
365 | |
c19d127e |
366 | Note: L<StackTrace|Catalyst::Plugin::StackTrace> output appears in your |
367 | browser, not in the console window from which you're running your |
368 | application, which is where logging output usually goes. |
4d583dd8 |
369 | |
4d583dd8 |
370 | =back |
371 | |
64ccd8a8 |
372 | Note that when specifying plugins on the C<use Catalyst> line, you can |
71dedf57 |
373 | omit C<Catalyst::Plugin::> from the name. Additionally, you can spread |
64ccd8a8 |
374 | the plugin names across multiple lines as shown here, or place them all |
375 | on one (or more) lines as with the default configuration. |
4d583dd8 |
376 | |
c608fae5 |
377 | B<TIP:> You may see examples that include the |
378 | L<Catalyst::Plugin::DefaultEnd|Catalyst::Plugin::DefaultEnd> |
8e956464 |
379 | plugins. As of Catalyst 5.7000, C<DefaultEnd> has been |
c608fae5 |
380 | deprecated in favor of |
381 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> |
382 | (as the name of the package suggests, C<RenderView> is not |
383 | a plugin, but an action). The purpose of both is essentially the same: |
208513f1 |
384 | forward processing to the view to be rendered. Applications generated |
8e956464 |
385 | under 5.7000 should automatically use C<RenderView> and "just work" |
208513f1 |
386 | for most applications. For more information on C<RenderView> and |
387 | the various options for forwarding to your view logic, please refer |
388 | to the "Using RenderView for the Default View" section under |
389 | "CATALYST VIEWS" below. |
c608fae5 |
390 | |
391 | |
4d583dd8 |
392 | =head1 DATABASE ACCESS WITH C<DBIx::Class> |
393 | |
64ccd8a8 |
394 | Catalyst can be used with virtually any form of persistent datastore |
14e6feb0 |
395 | available via Perl. For example, |
396 | L<Catalyst::Model::DBI|Catalyst::Model::DBI> can be used to |
397 | easily access databases through the traditional Perl C<DBI> interface. |
71dedf57 |
398 | However, most Catalyst applications use some form of ORM technology to |
64ccd8a8 |
399 | automatically create and save model objects as they are used. Although |
14e6feb0 |
400 | Tony Bowden's L<Class::DBI|Class::DBI> has been the traditional |
401 | Perl ORM engine, Matt Trout's L<DBIx::Class|DBIx::Class> (abbreviated |
402 | as "DBIC") has rapidly emerged as the Perl-based ORM technology of choice. |
403 | Most new Catalyst applications rely on DBIC, as will this tutorial. |
4d583dd8 |
404 | |
14e6feb0 |
405 | Note: See L<Catalyst:: Model::CDBI> for more information on using |
406 | Catalyst with L<Class::DBI|Class::DBI>. |
4d583dd8 |
407 | |
408 | =head2 Create a DBIC Schema File |
409 | |
64ccd8a8 |
410 | DBIx::Class uses a schema file to load other classes that represent the |
411 | tables in your database (DBIC refers to these "table objects" as "result |
71dedf57 |
412 | sources"; see L<DBIx::Class::ResultSource>). In this case, we want to |
413 | load the model object for the C<books>, C<book_authors>, and C<authors> |
414 | tables created in the previous step. |
4d583dd8 |
415 | |
416 | Open C<lib/MyAppDB.pm> in your editor and insert: |
417 | |
418 | package MyAppDB; |
419 | |
420 | =head1 NAME |
421 | |
71dedf57 |
422 | MyAppDB - DBIC Schema Class |
4d583dd8 |
423 | |
424 | =cut |
425 | |
426 | # Our schema needs to inherit from 'DBIx::Class::Schema' |
427 | use base qw/DBIx::Class::Schema/; |
428 | |
429 | # Need to load the DB Model classes here. |
430 | # You can use this syntax if you want: |
431 | # __PACKAGE__->load_classes(qw/Book BookAuthor Author/); |
432 | # Also, if you simply want to load all of the classes in a directory |
433 | # of the same name as your schema class (as we do here) you can use: |
434 | # __PACKAGE__->load_classes(qw//); |
435 | # But the variation below is more flexible in that it can be used to |
436 | # load from multiple namespaces. |
437 | __PACKAGE__->load_classes({ |
438 | MyAppDB => [qw/Book BookAuthor Author/] |
439 | }); |
440 | |
441 | 1; |
442 | |
64ccd8a8 |
443 | B<Note:> C<__PACKAGE__> is just a shorthand way of referencing the name |
444 | of the package where it is used. Therefore, in C<MyAppDB.pm>, |
14e6feb0 |
445 | C<__PACKAGE__> is equivalent to C<MyAppDB>. |
4d583dd8 |
446 | |
447 | |
448 | =head2 Create the DBIC "Result Source" Files |
449 | |
64ccd8a8 |
450 | In this step, we create "table classes" (again, these are called a |
71dedf57 |
451 | "result source" classes in DBIC) that act as model objects for the |
64ccd8a8 |
452 | C<books>, C<book_authors>, and C<authors> tables in our database. |
4d583dd8 |
453 | |
454 | First, create a directory to hold the class: |
455 | |
456 | $ mkdir lib/MyAppDB |
457 | |
458 | Then open C<lib/MyAppDB/Book.pm> in your editor and enter: |
459 | |
460 | package MyAppDB::Book; |
461 | |
462 | use base qw/DBIx::Class/; |
463 | |
464 | # Load required DBIC stuff |
465 | __PACKAGE__->load_components(qw/PK::Auto Core/); |
466 | # Set the table name |
467 | __PACKAGE__->table('books'); |
468 | # Set columns in table |
469 | __PACKAGE__->add_columns(qw/id title rating/); |
470 | # Set the primary key for the table |
471 | __PACKAGE__->set_primary_key(qw/id/); |
472 | |
473 | # |
474 | # Set relationships: |
475 | # |
476 | |
477 | # has_many(): |
478 | # args: |
479 | # 1) Name of relationship, DBIC will create accessor with this name |
480 | # 2) Name of the model class referenced by this relationship |
481 | # 3) Column name in *foreign* table |
482 | __PACKAGE__->has_many(book_authors => 'MyAppDB::BookAuthor', 'book_id'); |
483 | |
484 | # many_to_many(): |
485 | # args: |
486 | # 1) Name of relationship, DBIC will create accessor with this name |
487 | # 2) Name of has_many() relationship this many_to_many() is shortcut for |
488 | # 3) Name of belongs_to() relationship in model class of has_many() above |
489 | # You must already have the has_many() defined to use a many_to_many(). |
490 | __PACKAGE__->many_to_many(authors => 'book_authors', 'author'); |
491 | |
492 | |
493 | =head1 NAME |
494 | |
495 | MyAppDB::Book - A model object representing a book. |
496 | |
497 | =head1 DESCRIPTION |
498 | |
499 | This is an object that represents a row in the 'books' table of your application |
500 | database. It uses DBIx::Class (aka, DBIC) to do ORM. |
501 | |
502 | For Catalyst, this is designed to be used through MyApp::Model::MyAppDB. |
503 | Offline utilities may wish to use this class directly. |
504 | |
505 | =cut |
506 | |
507 | 1; |
508 | |
64ccd8a8 |
509 | This defines both a C<has_many> and a C<many_to_many> relationship. The |
510 | C<many_to_many> relationship is optional, but it makes it easier to map |
511 | a book to its collection of authors. Without it, we would have to |
512 | "walk" though the C<book_authors> table as in |
513 | C<$book-E<gt>book_authors-E<gt>first-E<gt>author-E<gt>last_name> (we |
514 | will see examples on how to use DBIC objects in your code soon, but note |
515 | that because C<$book-E<gt>book_authors> can return multiple authors, we |
71dedf57 |
516 | have to use C<first> to display a single author). C<many_to_many> allows |
517 | us to use the shorter C<$book-E<gt>authors-E<gt>first-E<gt>last_name>. |
518 | Note that you cannot define a C<many_to_many> relationship without also |
519 | having the C<has_many> relationship in place. |
4d583dd8 |
520 | |
521 | Next, open C<lib/MyAppDB/Author.pm> in your editor and enter: |
522 | |
523 | package MyAppDB::Author; |
524 | |
525 | use base qw/DBIx::Class/; |
526 | |
527 | # Load required DBIC stuff |
528 | __PACKAGE__->load_components(qw/PK::Auto Core/); |
529 | # Set the table name |
530 | __PACKAGE__->table('authors'); |
531 | # Set columns in table |
532 | __PACKAGE__->add_columns(qw/id first_name last_name/); |
533 | # Set the primary key for the table |
534 | __PACKAGE__->set_primary_key(qw/id/); |
208513f1 |
535 | |
4d583dd8 |
536 | # |
537 | # Set relationships: |
538 | # |
539 | |
540 | # has_many(): |
541 | # args: |
542 | # 1) Name of relationship, DBIC will create accessor with this name |
543 | # 2) Name of the model class referenced by this relationship |
544 | # 3) Column name in *foreign* table |
545 | __PACKAGE__->has_many(book_author => 'MyAppDB::BookAuthor', 'author_id'); |
546 | |
547 | # many_to_many(): |
548 | # args: |
549 | # 1) Name of relationship, DBIC will create accessor with this name |
550 | # 2) Name of has_many() relationship this many_to_many() is shortcut for |
551 | # 3) Name of belongs_to() relationship in model class of has_many() above |
552 | # You must already have the has_many() defined to use a many_to_many(). |
553 | __PACKAGE__->many_to_many(books => 'book_author', 'book'); |
554 | |
555 | |
556 | =head1 NAME |
557 | |
558 | MyAppDB::Author - A model object representing an author of a book (if a book has |
559 | multiple authors, each will be represented be separate Author object). |
560 | |
561 | =head1 DESCRIPTION |
562 | |
563 | This is an object that represents a row in the 'authors' table of your application |
564 | database. It uses DBIx::Class (aka, DBIC) to do ORM. |
565 | |
566 | For Catalyst, this is designed to be used through MyApp::Model::MyAppDB. |
567 | Offline utilities may wish to use this class directly. |
568 | |
569 | =cut |
570 | |
571 | 1; |
572 | |
573 | Finally, open C<lib/MyAppDB/BookAuthor.pm> in your editor and enter: |
574 | |
575 | package MyAppDB::BookAuthor; |
576 | |
577 | use base qw/DBIx::Class/; |
578 | |
579 | # Load required DBIC stuff |
580 | __PACKAGE__->load_components(qw/PK::Auto Core/); |
581 | # Set the table name |
582 | __PACKAGE__->table('book_authors'); |
583 | # Set columns in table |
584 | __PACKAGE__->add_columns(qw/book_id author_id/); |
585 | # Set the primary key for the table |
586 | __PACKAGE__->set_primary_key(qw/book_id author_id/); |
587 | |
588 | # |
589 | # Set relationships: |
590 | # |
591 | |
592 | # belongs_to(): |
593 | # args: |
594 | # 1) Name of relationship, DBIC will create accessor with this name |
595 | # 2) Name of the model class referenced by this relationship |
596 | # 3) Column name in *this* table |
597 | __PACKAGE__->belongs_to(book => 'MyAppDB::Book', 'book_id'); |
208513f1 |
598 | |
4d583dd8 |
599 | # belongs_to(): |
600 | # args: |
601 | # 1) Name of relationship, DBIC will create accessor with this name |
602 | # 2) Name of the model class referenced by this relationship |
603 | # 3) Column name in *this* table |
604 | __PACKAGE__->belongs_to(author => 'MyAppDB::Author', 'author_id'); |
605 | |
606 | |
607 | =head1 NAME |
608 | |
609 | MyAppDB::BookAuthor - A model object representing the JOIN between an author and |
610 | a book. |
611 | |
612 | =head1 DESCRIPTION |
613 | |
614 | This is an object that represents a row in the 'book_authors' table of your |
615 | application database. It uses DBIx::Class (aka, DBIC) to do ORM. |
208513f1 |
616 | |
4d583dd8 |
617 | You probably won't need to use this class directly -- it will be automatically |
618 | used by DBIC where joins are needed. |
619 | |
620 | For Catalyst, this is designed to be used through MyApp::Model::MyAppDB. |
621 | Offline utilities may wish to use this class directly. |
622 | |
623 | =cut |
624 | |
625 | 1; |
626 | |
64ccd8a8 |
627 | B<Note:> This sample application uses a plural form for the database |
628 | tables (e.g., C<books> and C<authors>) and a singular form for the model |
629 | objects (e.g., C<Book> and C<Author>); however, Catalyst places no |
630 | restrictions on the naming conventions you wish to use. |
4d583dd8 |
631 | |
4d583dd8 |
632 | =head2 Use C<Catalyst::Model::DBIC::Schema> To Load The Model Class |
633 | |
14e6feb0 |
634 | When L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> is |
64ccd8a8 |
635 | in use, Catalyst essentially reads an existing copy of your database |
636 | model and creates a new set of objects under C<MyApp::Model> for use |
637 | inside of Catalyst. |
4d583dd8 |
638 | |
14e6feb0 |
639 | B<Note:> With |
640 | L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> you |
641 | essentially end up with two sets of model classes (only one of which |
642 | you write... the other set is created automatically in memory when |
643 | your Catalyst application initializes). For this tutorial application, |
644 | the important points to remember are: you write the I<result source> |
645 | files in C<MyAppDB>, but I<within Catalyst> you use the I<automatically |
646 | created model classes> in C<MyApp::Model>. |
4d583dd8 |
647 | |
14e6feb0 |
648 | Use the |
649 | L<Catalyst::Helper::Model::DBIC::Schema|Catalyst::Helper::Model::DBIC::Schema> |
650 | helper script to create the model class that loads up the model we |
651 | created in the previous step: |
4d583dd8 |
652 | |
653 | $ script/myapp_create.pl model MyAppDB DBIC::Schema MyAppDB dbi:SQLite:myapp.db '' '' '{ AutoCommit => 1 }' |
208513f1 |
654 | exists "/root/dev/MyApp/script/../lib/MyApp/Model" |
655 | exists "/root/dev/MyApp/script/../t" |
656 | created "/root/dev/MyApp/script/../lib/MyApp/Model/MyAppDB.pm" |
657 | created "/root/dev/MyApp/script/../t/model_MyAppDB.t" |
658 | |
4d583dd8 |
659 | |
64ccd8a8 |
660 | Where the first C<MyAppDB> is the name of the class to be created by the |
661 | helper in C<lib/MyApp/Model> and the second C<MyAppDB> is the name of |
662 | existing schema file we created (in C<lib/MyAppDB.pm>). You can see |
663 | that the helper creates a model file under C<lib/MyApp/Model> (Catalyst |
664 | has a separate directory under C<lib/MyApp> for each of the three parts |
665 | of MVC: C<Model>, C<View>, and C<Controller> [although older Catalyst |
666 | applications often use the directories C<M>, C<V>, and C<C>]). |
4d583dd8 |
667 | |
668 | |
4d583dd8 |
669 | =head1 CREATE A CATALYST CONTROLLER |
670 | |
71dedf57 |
671 | Controllers are where you write methods that interact with user |
672 | input--typically, controller methods respond to C<GET> and C<POST> |
673 | messages from the user's web browser. |
4d583dd8 |
674 | |
71dedf57 |
675 | Use the Catalyst C<create> script to add a controller for book-related |
676 | actions: |
4d583dd8 |
677 | |
678 | $ script/myapp_create.pl controller Books |
208513f1 |
679 | exists "/root/dev/MyApp/script/../lib/MyApp/Controller" |
680 | exists "/root/dev/MyApp/script/../t" |
681 | created "/root/dev/MyApp/script/../lib/MyApp/Controller/Books.pm" |
682 | created "/root/dev/MyApp/script/../t/controller_Books.t" |
4d583dd8 |
683 | |
64ccd8a8 |
684 | Then edit C<lib/MyApp/Controller/Books.pm> and add the following method |
685 | to the controller: |
4d583dd8 |
686 | |
687 | =head2 list |
688 | |
689 | Fetch all book objects and pass to books/list.tt2 in stash to be displayed |
690 | |
691 | =cut |
692 | |
693 | sub list : Local { |
694 | # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst |
695 | # 'Context' that's used to 'glue together' the various components |
696 | # that make up the application |
697 | my ($self, $c) = @_; |
698 | |
699 | # Retrieve all of the book records as book model objects and store in the |
700 | # stash where they can be accessed by the TT template |
701 | $c->stash->{books} = [$c->model('MyAppDB::Book')->all]; |
702 | |
703 | # Set the TT template to use. You will almost always want to do this |
8112f931 |
704 | # in your action methods (actions methods respond to user input in |
705 | # your controllers). |
4d583dd8 |
706 | $c->stash->{template} = 'books/list.tt2'; |
707 | } |
708 | |
64ccd8a8 |
709 | B<Note:> Programmers experienced with object-oriented Perl should |
710 | recognize C<$self> as a reference to the object where this method was |
711 | called. On the other hand, C<$c> will be new to many Perl programmers |
712 | who have not used Catalyst before (it's sometimes written as |
713 | C<$context>). The Context object is automatically passed to all |
714 | Catalyst components. It is used to pass information between components |
715 | and provide access to Catalyst and plugin functionality. |
4d583dd8 |
716 | |
64ccd8a8 |
717 | B<TIP>: You may see the C<$c-E<gt>model('MyAppDB::Book')> used above |
718 | written as C<$c-E<gt>model('MyAppDB')-E<gt>resultset('Book)>. The two |
719 | are equivalent. |
4d583dd8 |
720 | |
64ccd8a8 |
721 | B<Note:> Catalyst actions are regular Perl methods, but they make use of |
14e6feb0 |
722 | Nicholas Clark's C<attributes> module (that's the C<: Local> next to the |
723 | C<sub list> in the code above) to provide additional information to the |
724 | Catalyst dispatcher logic. |
4d583dd8 |
725 | |
208513f1 |
726 | |
4d583dd8 |
727 | =head1 CATALYST VIEWS |
728 | |
71dedf57 |
729 | Views are where you render output, typically for display in the user's |
14e6feb0 |
730 | web browser, but also possibly using other display output-generation |
71dedf57 |
731 | systems. As with virtually every aspect of Catalyst, options abound |
732 | when it comes to the specific view technology you adopt inside your |
733 | application. However, most Catalyst applications use the Template |
734 | Toolkit, known as TT (for more information on TT, see |
208513f1 |
735 | L<http://www.template-toolkit.org>). Other popular view technologies |
71dedf57 |
736 | include Mason (L<http://www.masonhq.com> and |
14e6feb0 |
737 | L<http://www.masonbook.com>) and L<HTML::Template|HTML::Template> |
64ccd8a8 |
738 | (L<http://html-template.sourceforge.net>). |
4d583dd8 |
739 | |
70493af7 |
740 | =head2 Create a Catalyst View Using C<TTSite> |
4d583dd8 |
741 | |
742 | When using TT for the Catalyst view, there are two main helper scripts: |
743 | |
744 | =over 4 |
745 | |
746 | =item * |
747 | |
14e6feb0 |
748 | L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT> |
4d583dd8 |
749 | |
750 | =item * |
751 | |
14e6feb0 |
752 | L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite> |
4d583dd8 |
753 | |
754 | =back |
755 | |
64ccd8a8 |
756 | Both are similar, but C<TT> merely creates the C<lib/MyApp/View/TT.pm> |
757 | file and leaves the creation of any hierarchical template organization |
71dedf57 |
758 | entirely up to you. (It also creates a C<t/view_TT.t> file for testing; |
759 | test cases will be discussed in Part 7). The C<TTSite> helper creates a |
760 | modular and hierarchical view layout with separate Template Toolkit (TT) |
761 | files for common header and footer information, configuration values, a |
762 | CSS stylesheet, and more. |
4d583dd8 |
763 | |
64ccd8a8 |
764 | Enter the following command to enable the C<TTSite> style of view |
71dedf57 |
765 | rendering for this tutorial: |
4d583dd8 |
766 | |
767 | $ script/myapp_create.pl view TT TTSite |
208513f1 |
768 | exists "/root/dev/MyApp/script/../lib/MyApp/View" |
769 | exists "/root/dev/MyApp/script/../t" |
770 | created "/root/dev/MyApp/script/../lib/MyApp/View/TT.pm" |
771 | created "/root/dev/MyApp/script/../root/lib" |
772 | ... |
773 | created "/root/dev/MyApp/script/../root/src/ttsite.css" |
4d583dd8 |
774 | |
64ccd8a8 |
775 | This puts a number of files in the C<root/lib> and C<root/src> |
776 | directories that can be used to customize the look and feel of your |
777 | application. Also take a look at C<lib/MyApp/View/TT.pm> for config |
778 | values set by the C<TTSite> helper. |
779 | |
780 | B<TIP>: Note that TTSite does one thing that could confuse people who |
208513f1 |
781 | are used to the normal C<TT> Catalyst view: it redefines the Catalyst |
71dedf57 |
782 | context object in templates from its usual C<c> to C<Catalyst>. When |
783 | looking at other Catalyst examples, remember that they almost always use |
784 | C<c>. Note that Catalyst and TT I<do not complain> when you use the |
785 | wrong name to access the context object...TT simply outputs blanks for |
5c1f2a06 |
786 | that bogus logic (see next tip to change this behavior with TT C<DEBUG> |
787 | options). Finally, be aware that this change in name I<only> |
71dedf57 |
788 | applies to how the context object is accessed inside your TT templates; |
64ccd8a8 |
789 | your controllers will continue to use C<$c> (or whatever name you use |
71dedf57 |
790 | when fetching the reference from C<@_> inside your methods). (You can |
64ccd8a8 |
791 | change back to the "default" behavior be removing the C<CATALYST_VAR> |
792 | line from C<lib/MyApp/View/TT.pm>, but you will also have to edit |
793 | C<root/lib/config/main> and C<root/lib/config/url>. If you do this, be |
794 | careful not to have a collision between your own C<c> variable and the |
795 | Catalyst C<c> variable.) |
4d583dd8 |
796 | |
5c1f2a06 |
797 | B<TIP>: When troubleshooting TT it can be helpful to enable variable |
798 | C<DEBUG> options. You can do this in a Catalyst environment by adding |
799 | a C<DEBUG> line to the C<__PACKAGE__->config> declaration in |
8112f931 |
800 | C<lib/MyApp/View/TT.pm>: |
5c1f2a06 |
801 | |
802 | __PACKAGE__->config({ |
803 | CATALYST_VAR => 'Catalyst', |
804 | ... |
805 | DEBUG => 'undef', |
806 | ... |
807 | }); |
208513f1 |
808 | |
5c1f2a06 |
809 | There are a variety of options you can use, such as 'undef', 'all', |
810 | 'service', 'context', 'parser', 'provider', and 'service'. See |
811 | L<Template::Constants> for more information (remove the C<DEBUG_> |
812 | portion of the name shown in the TT docs and convert to lower case |
813 | for use inside Catalyst). |
814 | |
70493af7 |
815 | B<NOTE:> Please be sure to disable TT debug options before |
816 | continuing the tutorial (especially the 'undef' option -- leaving |
817 | this enabled will conflict with several of the conventions used |
818 | by this tutorial and TTSite to leave some variables undefined |
819 | on purpose). |
820 | |
5c1f2a06 |
821 | |
208513f1 |
822 | =head2 Using C<RenderView> for the Default View |
823 | |
824 | Once your controller logic has processed the request from a user, it |
825 | forwards processing to your view in order to generate the appropriate |
8e956464 |
826 | response output. Catalyst v5.7000 ships with a new mechanism, |
208513f1 |
827 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView>, that |
828 | automatically performs this operation. If you look in |
829 | C<lib/MyApp/Controller/Root.pm>, you should see the this empty |
830 | definition for the C<sub end> method: |
831 | |
832 | sub end : ActionClass('RenderView') {} |
833 | |
834 | The following bullet points provide a quick overview of the |
835 | C<RenderView> process: |
836 | |
837 | =over 4 |
838 | |
839 | =item * |
840 | |
841 | C<Root.pm> is designed to hold application-wide logic. |
842 | |
843 | =item * |
844 | |
845 | At the end of a given user request, Catalyst will call the most specific |
846 | C<end> method that's appropriate. For example, if the controller for a |
847 | request has an C<end> method defined, it will be called. However, if |
848 | the controller does not define a controller-specific C<end> method, the |
849 | "global" C<end> method in C<Root.pm> will be called. |
c608fae5 |
850 | |
208513f1 |
851 | =item * |
852 | |
853 | Because the definition includes an C<ActionClass> attribute, the |
854 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> logic |
565d379b |
855 | will be executed B<after> any code inside the definition of C<sub end> |
208513f1 |
856 | is run. See L<Catalyst::Manual::Actions|Catalyst::Manual::Actions> |
857 | for more information on C<ActionClass>. |
858 | |
859 | =item * |
860 | |
861 | Because C<sub end> is empty, this effectively just runs the default |
862 | logic in C<RenderView>. However, you can easily extend the |
863 | C<RenderView> logic by adding your own code inside the empty method body |
864 | (C<{}>) created by the Catalyst Helpers when we first ran the |
865 | C<catalyst.pl> to initialize our application. See |
866 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> for more |
867 | detailed information on how to extended C<RenderView> in C<sub end>. |
868 | |
869 | =back |
870 | |
871 | |
872 | =head3 The History Leading Up To C<RenderView> |
873 | |
874 | Although C<RenderView> strikes a nice balance between default |
875 | behavior and easy extensibility, it is a new feature that won't |
876 | appear in most existing Catalyst examples. This section provides |
877 | some brief background on the evolution of default view rendering |
878 | logic with an eye to how they can be migrated to C<RenderView>: |
c608fae5 |
879 | |
880 | =over 4 |
881 | |
882 | =item * |
883 | |
884 | Private C<end> Action in Application Class |
885 | |
886 | Older Catalyst-related documents often suggest that you add a "private |
887 | end action" to your application class (C<MyApp.pm>) or Root.pm |
888 | (C<MyApp/Controller/Root.pm>). These examples should be easily |
208513f1 |
889 | converted to L<RenderView|Catalyst::Action::RenderView> by simply adding |
8112f931 |
890 | the attribute C<:ActionClass('RenderView')> to the C<sub end> |
891 | definition. If end sub is defined in your application class |
892 | (C<MyApp.pm>), you should also migrate it to |
893 | C<MyApp/Controller/Root.pm>. |
c608fae5 |
894 | |
895 | =item * |
896 | |
897 | L<Catalyst::Plugin::DefaultEnd|Catalyst::Plugin::DefaultEnd> |
898 | |
208513f1 |
899 | C<DefaultEnd> represented the "next step" in passing processing from |
900 | your controller to your view. It has the advantage of only requiring |
901 | that C<DefaultEnd> be added to the list of plugins in C<lib/MyApp.pm>. |
902 | It also allowed you to add "dump_info=1" (precede with "?" or "&" |
903 | depending on where it is in the URL) to I<force> the debug screen at the |
904 | end of the Catalyst request processing cycle. However, it was more |
c9b77c06 |
905 | difficult to extend than the C<RenderView> mechanism, and is now |
906 | deprecated. |
c608fae5 |
907 | |
908 | =item * |
909 | |
910 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> |
911 | |
208513f1 |
912 | As discussed above, the current recommended approach to handling your |
913 | view logic relies on |
c608fae5 |
914 | L<Catalyst::Action::RenderView|Catalyst::Action::RenderView>. Although |
208513f1 |
915 | similar in first appearance to the "private end action" approach, it |
916 | utilizes Catalyst's "ActionClass" mechanism to provide both automatic |
917 | default behavior (you don't have to include a plugin as with |
918 | C<DefaultEnd>) and easy extensibility. As with C<DefaultEnd>, it allows |
919 | you to add "dump_info=1" (precede with "?" or "&" depending on where it |
920 | is in the URL) to I<force> the debug screen at the end of the Catalyst |
921 | request processing cycle. |
c608fae5 |
922 | |
923 | =back |
924 | |
208513f1 |
925 | It is recommended that all Catalyst applications use or migrate to |
926 | the C<RenderView> approach. |
c608fae5 |
927 | |
928 | |
4d583dd8 |
929 | =head2 Globally Customize Every View |
930 | |
64ccd8a8 |
931 | When using TTSite, files in the subdirectories of C<root/lib> can be |
932 | used to make changes that will appear in every view. For example, to |
933 | display optional status and error messages in every view, edit |
71dedf57 |
934 | C<root/lib/site/layout>, updating it to match the following (the two HTML |
64ccd8a8 |
935 | C<span> elements are new): |
4d583dd8 |
936 | |
937 | <div id="header">[% PROCESS site/header %]</div> |
938 | |
939 | <div id="content"> |
940 | <span class="message">[% status_msg %]</span> |
941 | <span class="error">[% error_msg %]</span> |
942 | [% content %] |
943 | </div> |
944 | |
945 | <div id="footer">[% PROCESS site/footer %]</div> |
946 | |
64ccd8a8 |
947 | If we set either message in the Catalyst stash (e.g., |
71dedf57 |
948 | C<$c-E<gt>stash-E<gt>{status_msg} = 'Request was successful!'>) it will |
949 | be displayed whenever any view used by that request is rendered. The |
950 | C<message> and C<error> CSS styles are automatically defined in |
951 | C<root/src/ttsite.css> and can be customized to suit your needs. |
4d583dd8 |
952 | |
64ccd8a8 |
953 | B<Note:> The Catalyst stash only lasts for a single HTTP request. If |
954 | you need to retain information across requests you can use |
14e6feb0 |
955 | L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> (we will use |
956 | Catalyst sessions in the Authentication part of the tutorial). |
4d583dd8 |
957 | |
958 | |
959 | =head2 Create a TT Template Page |
960 | |
64ccd8a8 |
961 | To add a new page of content to the TTSite view hierarchy, just create a |
962 | new C<.tt2> file in C<root/src>. Only include HTML markup that goes |
963 | inside the HTML <body> and </body> tags, TTSite will use the contents of |
964 | C<root/lib/site> to add the top and bottom. |
4d583dd8 |
965 | |
966 | First create a directory for book-related TT templates: |
967 | |
968 | $ mkdir root/src/books |
969 | |
970 | Then open C<root/src/books/list.tt2> in your editor and enter: |
971 | |
972 | [% # This is a TT comment. The '-' at the end "chomps" the newline. You won't -%] |
973 | [% # see this "chomping" in your browser because HTML ignores blank lines, but -%] |
974 | [% # it WILL eliminate a blank line if you view the HTML source. It's purely -%] |
975 | [%- # optional, but both the beginning and the ending TT tags support chomping. -%] |
976 | |
977 | [% # Provide a title to root/lib/site/header -%] |
978 | [% META title = 'Book List' -%] |
979 | |
980 | <table> |
981 | <tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr> |
982 | [% # Display each book in a table row %] |
983 | [% FOREACH book IN books -%] |
984 | <tr> |
985 | <td>[% book.title %]</td> |
986 | <td>[% book.rating %]</td> |
987 | <td> |
14e6feb0 |
988 | [% # First initialize a TT variable to hold a list. Then use a TT FOREACH -%] |
989 | [% # loop in 'side effect notation' to load just the last names of the -%] |
c9b77c06 |
990 | [% # authors into the list. Note that the 'push' TT vmethod does not -%] |
991 | [% # a value, so nothing will be printed here. But, if you have something -%] |
992 | [% # in TT that does return a method and you don't want it printed, you -%] |
993 | [% # can: 1) assign it to a bogus value, or 2) use the CALL keyword to -%] |
994 | [% # call it and discard the return value. -%] |
14e6feb0 |
995 | [% tt_authors = [ ]; |
c9b77c06 |
996 | tt_authors.push(author.last_name) FOREACH author = book.authors %] |
14e6feb0 |
997 | [% # Now use a TT 'virtual method' to display the author count in parens -%] |
998 | ([% tt_authors.size %]) |
c9b77c06 |
999 | [% # Use another TT vmethod to join & print the names & comma separators -%] |
14e6feb0 |
1000 | [% tt_authors.join(', ') %] |
4d583dd8 |
1001 | </td> |
1002 | </tr> |
1003 | [% END -%] |
1004 | </table> |
1005 | |
64ccd8a8 |
1006 | As indicated by the inline comments above, the C<META title> line uses |
1007 | TT's META feature to provide a title to C<root/lib/site/header>. |
1008 | Meanwhile, the outer C<FOREACH> loop iterates through each C<book> model |
1009 | object and prints the C<title> and C<rating> fields. An inner |
14e6feb0 |
1010 | C<FOREACH> loop prints the last name of each author in a comma-separated |
1011 | list within a single table cell. |
64ccd8a8 |
1012 | |
71dedf57 |
1013 | If you are new to TT, the C<[%> and C<%]> tags are used to delimit TT |
1014 | code. TT supports a wide variety of directives for "calling" other |
64ccd8a8 |
1015 | files, looping, conditional logic, etc. In general, TT simplifies the |
1016 | usual range of Perl operators down to the single dot (C<.>) operator. |
1017 | This applies to operations as diverse as method calls, hash lookups, and |
1018 | list index values (see |
1019 | L<http://www.template-toolkit.org/docs/default/Manual/Variables.html> |
1020 | for details and examples). In addition to the usual C<Template> module |
1021 | Pod documentation, you can access the TT manual at |
1022 | L<http://www.template-toolkit.org/docs/default/>. |
1023 | |
1024 | B<NOTE>: The C<TTSite> helper creates several TT files using an |
1025 | extension of C<.tt2>. Most other Catalyst and TT examples use an |
1026 | extension of C<.tt>. You can use either extension (or no extension at |
1027 | all) with TTSite and TT, just be sure to use the appropriate extension |
1028 | for both the file itself I<and> the C<$c-E<gt>stash-E<gt>{template} = |
1029 | ...> line in your controller. This document will use C<.tt2> for |
1030 | consistency with the files already created by the C<TTSite> helper. |
4d583dd8 |
1031 | |
1032 | |
4d583dd8 |
1033 | =head1 RUN THE APPLICATION |
1034 | |
64ccd8a8 |
1035 | First, let's enable an environment variable option that causes |
1036 | DBIx::Class to dump the SQL statements it's using to access the database |
1037 | (this option can provide extremely helpful troubleshooting information): |
4d583dd8 |
1038 | |
bc384c9d |
1039 | $ export DBIC_TRACE=1 |
1040 | |
1041 | B<NOTE>: You can also use the older |
1042 | C<export DBIX_CLASS_STORAGE_DBI_DEBUG=1>, that that's a lot more to |
1043 | type. |
4d583dd8 |
1044 | |
cc548726 |
1045 | This assumes you are using BASH as your shell -- adjust accordingly if |
1046 | you are using a different shell (for example, under tcsh, use |
1047 | C<setenv DBIX_CLASS_STORAGE_DBI_DEBUG 1>). |
1048 | |
64ccd8a8 |
1049 | B<NOTE>: You can also set this in your code using |
1050 | C<$class-E<gt>storage-E<gt>debug(1);>. See |
71dedf57 |
1051 | L<DBIx::Class::Manual::Troubleshooting> for details (including options |
1052 | to log to file instead of displaying to the Catalyst development server |
1053 | log). |
4d583dd8 |
1054 | |
1055 | Then run the Catalyst "demo server" script: |
1056 | |
1057 | $ script/myapp_server.pl |
1058 | |
208513f1 |
1059 | Your development server log output should display something like: |
4d583dd8 |
1060 | |
1061 | $ script/myapp_server.pl |
208513f1 |
1062 | [debug] Debug messages enabled |
1063 | [debug] Loaded plugins: |
1064 | .----------------------------------------------------------------------------. |
1065 | | Catalyst::Plugin::ConfigLoader 0.06 | |
1066 | | Catalyst::Plugin::StackTrace 0.04 | |
1067 | | Catalyst::Plugin::Static::Simple 0.14 | |
1068 | '----------------------------------------------------------------------------' |
4d583dd8 |
1069 | |
208513f1 |
1070 | [debug] Loaded dispatcher "Catalyst::Dispatcher" |
1071 | [debug] Loaded engine "Catalyst::Engine::HTTP" |
1072 | [debug] Found home "/home/me/MyApp" |
1073 | [debug] Loaded components: |
1074 | .-----------------------------------------------------------------+----------. |
1075 | | Class | Type | |
1076 | +-----------------------------------------------------------------+----------+ |
1077 | | MyApp::Controller::Books | instance | |
1078 | | MyApp::Controller::Root | instance | |
1079 | | MyApp::Model::MyAppDB | instance | |
1080 | | MyApp::Model::MyAppDB::Author | class | |
1081 | | MyApp::Model::MyAppDB::Book | class | |
1082 | | MyApp::Model::MyAppDB::BookAuthor | class | |
1083 | | MyApp::View::TT | instance | |
1084 | '-----------------------------------------------------------------+----------' |
4d583dd8 |
1085 | |
208513f1 |
1086 | [debug] Loaded Private actions: |
1087 | .----------------------+--------------------------------------+--------------. |
1088 | | Private | Class | Method | |
1089 | +----------------------+--------------------------------------+--------------+ |
1090 | | /default | MyApp::Controller::Root | default | |
1091 | | /end | MyApp::Controller::Root | end | |
1092 | | /books/index | MyApp::Controller::Books | index | |
1093 | | /books/list | MyApp::Controller::Books | list | |
1094 | '----------------------+--------------------------------------+--------------' |
4d583dd8 |
1095 | |
208513f1 |
1096 | [debug] Loaded Path actions: |
1097 | .-------------------------------------+--------------------------------------. |
1098 | | Path | Private | |
1099 | +-------------------------------------+--------------------------------------+ |
1100 | | /books/list | /books/list | |
1101 | '-------------------------------------+--------------------------------------' |
4d583dd8 |
1102 | |
8e956464 |
1103 | [info] MyApp powered by Catalyst 5.7000 |
208513f1 |
1104 | You can connect to your server at http://localhost.localdomain:3000 |
4d583dd8 |
1105 | |
1106 | Some things you should note in the output above: |
1107 | |
1108 | =over 4 |
1109 | |
1110 | =item * |
1111 | |
64ccd8a8 |
1112 | Catalyst::Model::DBIC::Schema took our C<MyAppDB::Book> and made it |
1113 | C<MyApp::Model::MyAppDB::Book> (and similar actions were performed on |
1114 | C<MyAppDB::Author> and C<MyAppDB::BookAuthor>). |
4d583dd8 |
1115 | |
1116 | =item * |
1117 | |
64ccd8a8 |
1118 | The "list" action in our Books controller showed up with a path of |
1119 | C</books/list>. |
4d583dd8 |
1120 | |
1121 | =back |
1122 | |
64ccd8a8 |
1123 | Point your browser to L<http://localhost:3000> and you should still get |
1124 | the Catalyst welcome page. |
4d583dd8 |
1125 | |
64ccd8a8 |
1126 | Next, to view the book list, change the URL in your browser to |
1127 | L<http://localhost:3000/books/list>. You should get a list of the five |
1128 | books loaded by the C<myapp01.sql> script above, with TTSite providing |
1129 | the formatting for the very simple output we generated in our template. |
1130 | The count and space-separated list of author last names appear on the |
1131 | end of each row. |
4d583dd8 |
1132 | |
64ccd8a8 |
1133 | Also notice in the output of the C<script/myapp_server.pl> that DBIC |
1134 | used the following SQL to retrieve the data: |
4d583dd8 |
1135 | |
1136 | SELECT me.id, me.title, me.rating FROM books me |
1137 | |
64ccd8a8 |
1138 | Along with a list of the following commands to retrieve the authors for |
1139 | each book (the lines have been "word wrapped" here to improve |
1140 | legibility): |
4d583dd8 |
1141 | |
1142 | SELECT author.id, author.first_name, author.last_name |
1143 | FROM book_authors me |
1144 | JOIN authors author ON ( author.id = me.author_id ) |
1145 | WHERE ( me.book_id = ? ): `1' |
1146 | |
208513f1 |
1147 | You should see 5 such lines of debug output as DBIC fetches the author |
1148 | information for each book. |
4d583dd8 |
1149 | |
1150 | |
770fdaa9 |
1151 | =head1 USING THE DEFAULT TEMPLATE NAME |
1152 | |
1153 | By default, C<Catalyst::View::TT> will look for a template that uses the |
1154 | same name as your controller action, allowing you to save the step of |
1155 | manually specifying the template name in each action. For example, this |
1156 | would allow us to remove (or comment out) the |
1157 | C<$c-E<gt>stash-E<gt>{template} = 'books/list.tt2';> line of our |
1158 | C<list> action in the Books controller. Open |
1159 | C<lib/MyApp/Controller/Books.pm> in your editor and update it to |
1160 | match the following: |
1161 | |
1162 | =head2 list |
1163 | |
1164 | Fetch all book objects and pass to books/list.tt2 in stash to be displayed |
1165 | |
1166 | =cut |
1167 | |
1168 | sub list : Local { |
1169 | # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst |
1170 | # 'Context' that's used to 'glue together' the various components |
1171 | # that make up the application |
1172 | my ($self, $c) = @_; |
1173 | |
1174 | # Retrieve all of the book records as book model objects and store in the |
1175 | # stash where they can be accessed by the TT template |
1176 | $c->stash->{books} = [$c->model('MyAppDB::Book')->all]; |
1177 | |
1178 | # Automatically look for a template of 'books/list.tt2' template |
1179 | # (if TEMPLATE_EXTENSION is set to '.tt2') |
1180 | } |
1181 | |
1182 | C<Catalyst::View::TT> defaults to looking for a template with no |
1183 | extension. In our case, we need to override this to look for an |
1184 | extension of C<.tt2>. Open C<lib/MyApp/View/TT.pm> and add the |
1185 | C<TEMPLATE_EXTENSION> definition as follows: |
1186 | |
1187 | __PACKAGE__->config({ |
1188 | CATALYST_VAR => 'Catalyst', |
1189 | INCLUDE_PATH => [ |
1190 | MyApp->path_to( 'root', 'src' ), |
1191 | MyApp->path_to( 'root', 'lib' ) |
1192 | ], |
1193 | PRE_PROCESS => 'config/main', |
1194 | WRAPPER => 'site/wrapper', |
1195 | ERROR => 'error.tt2', |
1196 | TIMER => 0, |
1197 | TEMPLATE_EXTENSION => '.tt2', |
1198 | }); |
1199 | |
1200 | You should now be able to restart the development server as per the |
1201 | previous section and access the L<http://localhost:3000/books/list> |
1202 | as before. |
1203 | |
1204 | Although this can be a valuable technique to establish a default |
1205 | template for each of your actions, the remainder of the tutorial |
1206 | will manually assign the template name to |
1207 | C<$c-E<gt>stash-E<gt>{template}> in each action in order to make |
1208 | the logic as conspicuous as possible. |
1209 | |
1210 | |
4d583dd8 |
1211 | =head1 AUTHOR |
1212 | |
1213 | Kennedy Clark, C<hkclark@gmail.com> |
1214 | |
c608fae5 |
1215 | Please report any errors, issues or suggestions to the author. The |
7d310f12 |
1216 | most recent version of the Catalyst Tutorial can be found at |
c608fae5 |
1217 | L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>. |
4d583dd8 |
1218 | |
71dedf57 |
1219 | Copyright 2006, Kennedy Clark, under Creative Commons License |
1220 | (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>). |
4d583dd8 |
1221 | |