Commit | Line | Data |
b80f58e5 |
1 | =head1 NAME |
2 | |
3ab6187c |
3 | Catalyst::Manual::Tutorial::09_AdvancedCRUD::09_FormFu - Catalyst Tutorial - Chapter 9: Advanced CRUD - FormFu |
b80f58e5 |
4 | |
5 | |
6 | =head1 OVERVIEW |
7 | |
4b4d3884 |
8 | This is B<Chapter 9 of 10> for the Catalyst tutorial. |
b80f58e5 |
9 | |
10 | L<Tutorial Overview|Catalyst::Manual::Tutorial> |
11 | |
12 | =over 4 |
13 | |
14 | =item 1 |
15 | |
3ab6187c |
16 | L<Introduction|Catalyst::Manual::Tutorial::01_Intro> |
b80f58e5 |
17 | |
18 | =item 2 |
19 | |
3ab6187c |
20 | L<Catalyst Basics|Catalyst::Manual::Tutorial::02_CatalystBasics> |
b80f58e5 |
21 | |
22 | =item 3 |
23 | |
3ab6187c |
24 | L<More Catalyst Basics|Catalyst::Manual::Tutorial::03_MoreCatalystBasics> |
b80f58e5 |
25 | |
26 | =item 4 |
27 | |
3ab6187c |
28 | L<Basic CRUD|Catalyst::Manual::Tutorial::04_BasicCRUD> |
b80f58e5 |
29 | |
30 | =item 5 |
31 | |
3ab6187c |
32 | L<Authentication|Catalyst::Manual::Tutorial::05_Authentication> |
b80f58e5 |
33 | |
34 | =item 6 |
35 | |
3ab6187c |
36 | L<Authorization|Catalyst::Manual::Tutorial::06_Authorization> |
b80f58e5 |
37 | |
38 | =item 7 |
39 | |
3ab6187c |
40 | L<Debugging|Catalyst::Manual::Tutorial::07_Debugging> |
b80f58e5 |
41 | |
42 | =item 8 |
43 | |
3ab6187c |
44 | L<Testing|Catalyst::Manual::Tutorial::08_Testing> |
b80f58e5 |
45 | |
46 | =item 9 |
47 | |
3ab6187c |
48 | B<09_Advanced CRUD::09_FormFu> |
b80f58e5 |
49 | |
50 | =item 10 |
51 | |
3ab6187c |
52 | L<Appendices|Catalyst::Manual::Tutorial::10_Appendices> |
b80f58e5 |
53 | |
54 | =back |
55 | |
56 | |
57 | =head1 DESCRIPTION |
58 | |
16d0d396 |
59 | This portion of the tutorial explores L<HTML::FormFu> and how it can be |
60 | used to manage forms, perform validation of form input, as well as save |
c016427f |
61 | and restore data to/from the database. |
cca5cd98 |
62 | |
16d0d396 |
63 | See L<Catalyst::Manual::Tutorial::09_AdvancedCRUD> for additional form |
64 | management options other than L<HTML::FormFu>. |
cca5cd98 |
65 | |
b1b6582a |
66 | Source code for the tutorial in included in the F</root/Final> directory |
67 | of the Tutorial Virtual machine (one subdirectory per chapter). There |
68 | are also instructions for downloading the code in |
69 | L<Catalyst::Manual::Tutorial::01_Intro>. |
70 | |
cca5cd98 |
71 | |
8a472b34 |
72 | =head1 HTML::FormFu FORM CREATION |
cca5cd98 |
73 | |
16d0d396 |
74 | This section looks at how L<HTML::FormFu> can be used to add additional |
c016427f |
75 | functionality to the manually created form from |
76 | L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD>. |
cca5cd98 |
77 | |
78 | |
8a472b34 |
79 | =head2 Inherit From Catalyst::Controller::HTML::FormFu |
cca5cd98 |
80 | |
81 | First, change your C<lib/MyApp/Controller/Books.pm> to inherit from |
16d0d396 |
82 | L<Catalyst::Controller::HTML::FormFu> by changing the C<extends> line |
83 | from the default of: |
cca5cd98 |
84 | |
96a87356 |
85 | BEGIN {extends 'Catalyst::Controller'; } |
cca5cd98 |
86 | |
87 | to use the FormFu base controller class: |
88 | |
96a87356 |
89 | BEGIN {extends 'Catalyst::Controller::HTML::FormFu'; } |
cca5cd98 |
90 | |
9372a4db |
91 | Don't forget to add: |
92 | |
c016427f |
93 | requires 'HTML::FormFu'; |
9372a4db |
94 | requires 'Catalyst::Controller::HTML::FormFu'; |
c016427f |
95 | requires 'requires 'HTML::FormFu::Model::DBIC';'; |
9372a4db |
96 | |
97 | to your C<Makefile.PL>. |
cca5cd98 |
98 | |
c016427f |
99 | |
cca5cd98 |
100 | =head2 Add Action to Display and Save the Form |
101 | |
102 | Open C<lib/MyApp/Controller/Books.pm> in your editor and add the |
103 | following method: |
104 | |
d682d02d |
105 | =head2 formfu_create |
cca5cd98 |
106 | |
59d6a035 |
107 | Use HTML::FormFu to create a new book |
cca5cd98 |
108 | |
109 | =cut |
110 | |
ccc9b2bc |
111 | sub formfu_create :Chained('base') :PathPart('formfu_create') :Args(0) :FormConfig { |
cca5cd98 |
112 | my ($self, $c) = @_; |
113 | |
114 | # Get the form that the :FormConfig attribute saved in the stash |
115 | my $form = $c->stash->{form}; |
3dba69ab |
116 | |
166c90a0 |
117 | # Check if the form has been submitted (vs. displaying the initial |
118 | # form) and if the data passed validation. "submitted_and_valid" |
cca5cd98 |
119 | # is shorthand for "$form->submitted && !$form->has_errors" |
120 | if ($form->submitted_and_valid) { |
121 | # Create a new book |
3b1fa91b |
122 | my $book = $c->model('DB::Book')->new_result({}); |
cca5cd98 |
123 | # Save the form data for the book |
aee27483 |
124 | $form->model->update($book); |
c016427f |
125 | # Set a status message for the user & return to books list |
126 | $c->response->redirect($c->uri_for($self->action_for('list'), |
127 | {mid => $c->set_status_msg("Book created")})); |
cca5cd98 |
128 | $c->detach; |
9cb64a37 |
129 | } else { |
130 | # Get the authors from the DB |
3b1fa91b |
131 | my @author_objs = $c->model("DB::Author")->all(); |
9cb64a37 |
132 | # Create an array of arrayrefs where each arrayref is an author |
133 | my @authors; |
acbd7bdd |
134 | foreach (sort {$a->last_name cmp $b->last_name} @author_objs) { |
9cb64a37 |
135 | push(@authors, [$_->id, $_->last_name]); |
136 | } |
137 | # Get the select added by the config file |
138 | my $select = $form->get_element({type => 'Select'}); |
139 | # Add the authors to it |
140 | $select->options(\@authors); |
141 | } |
3dba69ab |
142 | |
cca5cd98 |
143 | # Set the template |
766e64ba |
144 | $c->stash(template => 'books/formfu_create.tt2'); |
cca5cd98 |
145 | } |
146 | |
147 | |
148 | =head2 Create a Form Config File |
149 | |
150 | Although C<HTML::FormFu> supports any configuration file handled by |
16d0d396 |
151 | L<Config::Any>, most people tend to use YAML. First create a directory |
152 | to hold your form configuration files: |
cca5cd98 |
153 | |
c016427f |
154 | $ mkdir -p root/forms/books |
cca5cd98 |
155 | |
16d0d396 |
156 | Then create the file C<root/forms/books/formfu_create.yml> and enter the |
cca5cd98 |
157 | following text: |
158 | |
159 | --- |
9cb64a37 |
160 | # indicator is the field that is used to test for form submission |
cca5cd98 |
161 | indicator: submit |
9cb64a37 |
162 | # Start listing the form elements |
cca5cd98 |
163 | elements: |
9cb64a37 |
164 | # The first element will be a text field for the title |
cca5cd98 |
165 | - type: Text |
166 | name: title |
167 | label: Title |
9cb64a37 |
168 | # This is an optional 'mouse over' title pop-up |
cca5cd98 |
169 | attributes: |
170 | title: Enter a book title here |
d682d02d |
171 | |
172 | # Another text field for the numeric rating |
cca5cd98 |
173 | - type: Text |
174 | name: rating |
175 | label: Rating |
176 | attributes: |
177 | title: Enter a rating between 1 and 5 here |
d682d02d |
178 | |
179 | # Add a drop-down list for the author selection. Note that we will |
180 | # dynamically fill in all the authors from the controller but we |
181 | # could manually set items in the drop-list by adding this YAML code: |
182 | # options: |
183 | # - [ '1', 'Bastien' ] |
184 | # - [ '2', 'Nasseh' ] |
185 | - type: Select |
186 | name: authors |
187 | label: Author |
188 | |
9cb64a37 |
189 | # The submit button |
cca5cd98 |
190 | - type: Submit |
191 | name: submit |
192 | value: Submit |
193 | |
16d0d396 |
194 | B<NOTE:> Copying and pasting YAML from Perl documentation is sometimes |
1fba2369 |
195 | tricky. See the L<Config::General Config for this tutorial> section of |
196 | this document for a more foolproof config format. |
0171092f |
197 | |
cca5cd98 |
198 | |
199 | =head2 Update the CSS |
200 | |
16d0d396 |
201 | Edit C<root/static/css/main.css> and add the following lines to the |
202 | bottom of the file: |
cca5cd98 |
203 | |
acbd7bdd |
204 | ... |
d682d02d |
205 | input { |
cca5cd98 |
206 | display: block; |
207 | } |
d682d02d |
208 | select { |
cca5cd98 |
209 | display: block; |
210 | } |
d682d02d |
211 | .submit { |
212 | padding-top: .5em; |
213 | display: block; |
cca5cd98 |
214 | } |
215 | |
0a699832 |
216 | These changes will display form elements vertically. |
cca5cd98 |
217 | |
218 | |
219 | =head2 Create a Template Page To Display The Form |
220 | |
16d0d396 |
221 | Open C<root/src/books/formfu_create.tt2> in your editor and enter the |
222 | following: |
cca5cd98 |
223 | |
224 | [% META title = 'Create/Update Book' %] |
225 | |
226 | [%# Render the HTML::FormFu Form %] |
227 | [% form %] |
228 | |
aeaa7db5 |
229 | <p><a href="[% c.uri_for(c.controller.action_for('list')) |
230 | %]">Return to book list</a></p> |
cca5cd98 |
231 | |
232 | |
233 | =head2 Add Links for Create and Update via C<HTML::FormFu> |
234 | |
235 | Open C<root/src/books/list.tt2> in your editor and add the following to |
236 | the bottom of the existing file: |
237 | |
acbd7bdd |
238 | ... |
cca5cd98 |
239 | <p> |
240 | HTML::FormFu: |
e075db0c |
241 | <a href="[% c.uri_for(c.controller.action_for('formfu_create')) %]">Create</a> |
cca5cd98 |
242 | </p> |
243 | |
16d0d396 |
244 | This adds a new link to the bottom of the book list page that we can use |
245 | to easily launch our HTML::FormFu-based form. |
d682d02d |
246 | |
cca5cd98 |
247 | |
8a472b34 |
248 | =head2 Test The HTML::FormFu Create Form |
cca5cd98 |
249 | |
aeaa7db5 |
250 | Make sure the server is running with the "-r" restart option: |
cca5cd98 |
251 | |
aeaa7db5 |
252 | $ script/myapp_server.pl -r |
cca5cd98 |
253 | |
0909d46f |
254 | Login as C<test01> (password: mypass). Once at the Book List page, |
255 | click the new HTML::FormFu "Create" link at the bottom to display the |
3b1fa91b |
256 | form. Fill in the following values: |
d682d02d |
257 | |
aeaa7db5 |
258 | Title: Internetworking with TCP/IP Vol. II |
259 | Rating: 4 |
260 | Author: Comer |
261 | |
c016427f |
262 | Click the "Submit" button, and you will be returned to the Book List page |
3b1fa91b |
263 | with a "Book created" status message displayed. |
264 | |
265 | Also note that this implementation allows you to create books with any |
16d0d396 |
266 | bogus information. Although we have constrained the authors with the |
267 | drop-down list (note that this isn't bulletproof because we still have |
268 | not prevented a user from "hacking" the form to specify other values), |
269 | there are no restrictions on items such as the length of the title (for |
270 | example, you can create a one-letter title) and the value of the rating |
271 | (you can use any number you want, and even non-numeric values with |
59d6a035 |
272 | SQLite). The next section will address this concern. |
cca5cd98 |
273 | |
274 | B<Note:> Depending on the database you are using and how you established |
275 | the columns in your tables, the database could obviously provide various |
276 | levels of "type enforcement" on your data. The key point being made in |
277 | the previous paragraph is that the I<web application> itself is not |
278 | performing any validation. |
279 | |
280 | |
8a472b34 |
281 | =head1 HTML::FormFu VALIDATION AND FILTERING |
cca5cd98 |
282 | |
16d0d396 |
283 | Although the use of L<HTML::FormFu> in the previous section did provide |
284 | an automated mechanism to build the form, the real power of this module |
285 | stems from functionality that can automatically validate and filter the |
286 | user input. Validation uses constraints to be sure that users input |
287 | appropriate data (for example, that the email field of a form contains a |
288 | valid email address). Filtering can also be used to remove extraneous |
289 | whitespace from fields or to escape meta-characters in user input. |
cca5cd98 |
290 | |
cca5cd98 |
291 | |
d682d02d |
292 | =head2 Add Constraints |
cca5cd98 |
293 | |
16d0d396 |
294 | Open C<root/forms/books/formfu_create.yml> in your editor and update it |
cca5cd98 |
295 | to match: |
296 | |
297 | --- |
9cb64a37 |
298 | # indicator is the field that is used to test for form submission |
cca5cd98 |
299 | indicator: submit |
9cb64a37 |
300 | # Start listing the form elements |
cca5cd98 |
301 | elements: |
9cb64a37 |
302 | # The first element will be a text field for the title |
cca5cd98 |
303 | - type: Text |
304 | name: title |
305 | label: Title |
9cb64a37 |
306 | # This is an optional 'mouse over' title pop-up |
cca5cd98 |
307 | attributes: |
308 | title: Enter a book title here |
9cb64a37 |
309 | # Add constraints for the field |
cca5cd98 |
310 | constraints: |
59d6a035 |
311 | # Force the length to be between 5 and 40 chars |
cca5cd98 |
312 | - type: Length |
d682d02d |
313 | min: 5 |
59d6a035 |
314 | max: 40 |
9cb64a37 |
315 | # Override the default of 'Invalid input' |
59d6a035 |
316 | message: Length must be between 5 and 40 characters |
ab0db558 |
317 | |
d682d02d |
318 | # Another text field for the numeric rating |
cca5cd98 |
319 | - type: Text |
320 | name: rating |
321 | label: Rating |
322 | attributes: |
323 | title: Enter a rating between 1 and 5 here |
d682d02d |
324 | # Use Filter to clean up the input data |
ab0db558 |
325 | # Could use 'NonNumeric' below, but since Filters apply *before* |
326 | # constraints, it would conflict with the 'Integer' constraint below. |
327 | # So let's skip this and just use the constraint. |
328 | #filter: |
d682d02d |
329 | # Remove everything except digits |
ab0db558 |
330 | #- NonNumeric |
d682d02d |
331 | # Add constraints to the field |
cca5cd98 |
332 | constraints: |
9cb64a37 |
333 | # Make sure it's a number |
ab0db558 |
334 | - type: Integer |
335 | message: "Required. Digits only, please." |
336 | # Check the min & max values |
337 | - type: Range |
338 | min: 1 |
339 | max: 5 |
340 | message: "Must be between 1 and 5." |
d682d02d |
341 | |
342 | # Add a select list for the author selection. Note that we will |
343 | # dynamically fill in all the authors from the controller but we |
344 | # could manually set items in the select by adding this YAML code: |
345 | # options: |
346 | # - [ '1', 'Bastien' ] |
347 | # - [ '2', 'Nasseh' ] |
348 | - type: Select |
349 | name: authors |
350 | label: Author |
351 | # Convert the drop-down to a multi-select list |
352 | multiple: 1 |
353 | # Display 3 entries (user can scroll to see others) |
354 | size: 3 |
355 | # One could argue we don't need to do filters or constraints for |
356 | # a select list, but it's smart to do validation and sanity |
357 | # checks on this data in case a user "hacks" the input |
d682d02d |
358 | # Add constraints to the field |
359 | constraints: |
d682d02d |
360 | # Make sure it's a number |
361 | - Integer |
362 | |
9cb64a37 |
363 | # The submit button |
cca5cd98 |
364 | - type: Submit |
365 | name: submit |
366 | value: Submit |
d682d02d |
367 | |
f279297a |
368 | # Global filters and constraints. |
cca5cd98 |
369 | constraints: |
c016427f |
370 | # The user cannot leave any fields blank |
371 | - Required |
372 | # If not all fields are required, move the Required constraint to the |
373 | # fields that are |
f279297a |
374 | filter: |
c016427f |
375 | # Remove whitespace at both ends |
376 | - TrimEdges |
377 | # Escape HTML characters for safety |
378 | - HTMLEscape |
cca5cd98 |
379 | |
16d0d396 |
380 | B<NOTE:> Copying and pasting YAML from Perl documentation is sometimes |
1fba2369 |
381 | tricky. See the L<Config::General Config for this tutorial> section of |
382 | this document for a more foolproof config format. |
0171092f |
383 | |
d682d02d |
384 | The main changes are: |
385 | |
386 | =over 4 |
387 | |
388 | =item * |
389 | |
390 | The C<Select> element for C<authors> is changed from a single-select |
16d0d396 |
391 | drop-down to a multi-select list by adding configuration for the |
d682d02d |
392 | C<multiple> and C<size> options in C<formfu_create.yml>. |
393 | |
394 | =item * |
395 | |
396 | Constraints are added to provide validation of the user input. See |
16d0d396 |
397 | L<HTML::FormFu::Constraint> for other constraints that are available. |
d682d02d |
398 | |
399 | =item * |
400 | |
16d0d396 |
401 | A variety of filters are run on every field to remove and escape |
402 | unwanted input. See L<HTML::FormFu::Filter> for more filter options. |
d682d02d |
403 | |
404 | =back |
405 | |
406 | |
407 | =head2 Try Out the Updated Form |
408 | |
16d0d396 |
409 | Make sure you are still logged in as C<test01> and try adding a book |
410 | with various errors: title less than 5 characters, non-numeric rating, a |
411 | rating of 0 or 6, etc. Also try selecting one, two, and zero authors. |
412 | When you click Submit, the HTML::FormFu C<constraint> items will |
413 | validate the logic and insert feedback as appropriate. Try adding blank |
414 | spaces at the front or the back of the title and note that it will be |
59d6a035 |
415 | removed. |
416 | |
c016427f |
417 | Note that you can update your FormFu YAML forms and the development |
418 | server does not need to reload -- the form definition is read from |
419 | the YAML file each time a controller action uses it. |
420 | |
59d6a035 |
421 | |
422 | =head1 CREATE AND UPDATE/EDIT ACTION |
423 | |
16d0d396 |
424 | Let's expand the work done above to add an edit action. First, open |
425 | C<lib/MyApp/Controller/Books.pm> and add the following method to the |
59d6a035 |
426 | bottom: |
427 | |
428 | =head2 formfu_edit |
429 | |
430 | Use HTML::FormFu to update an existing book |
431 | |
432 | =cut |
433 | |
acbd7bdd |
434 | sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0) |
435 | :FormConfig('books/formfu_create.yml') { |
ccc9b2bc |
436 | my ($self, $c) = @_; |
59d6a035 |
437 | |
faa6f5bc |
438 | # Get the specified book already saved by the 'object' method |
ccc9b2bc |
439 | my $book = $c->stash->{object}; |
59d6a035 |
440 | |
441 | # Make sure we were able to get a book |
442 | unless ($book) { |
c016427f |
443 | # Set an error message for the user & return to books list |
444 | $c->response->redirect($c->uri_for($self->action_for('list'), |
445 | {mid => $c->set_error_msg("Invalid book -- Cannot edit")})); |
59d6a035 |
446 | $c->detach; |
447 | } |
448 | |
449 | # Get the form that the :FormConfig attribute saved in the stash |
450 | my $form = $c->stash->{form}; |
451 | |
166c90a0 |
452 | # Check if the form has been submitted (vs. displaying the initial |
453 | # form) and if the data passed validation. "submitted_and_valid" |
59d6a035 |
454 | # is shorthand for "$form->submitted && !$form->has_errors" |
455 | if ($form->submitted_and_valid) { |
456 | # Save the form data for the book |
aee27483 |
457 | $form->model->update($book); |
59d6a035 |
458 | # Set a status message for the user |
c016427f |
459 | # Set a status message for the user & return to books list |
460 | $c->response->redirect($c->uri_for($self->action_for('list'), |
461 | {mid => $c->set_status_msg("Book edited")})); |
59d6a035 |
462 | $c->detach; |
463 | } else { |
464 | # Get the authors from the DB |
3b1fa91b |
465 | my @author_objs = $c->model("DB::Author")->all(); |
59d6a035 |
466 | # Create an array of arrayrefs where each arrayref is an author |
467 | my @authors; |
ccc9b2bc |
468 | foreach (sort {$a->last_name cmp $b->last_name} @author_objs) { |
59d6a035 |
469 | push(@authors, [$_->id, $_->last_name]); |
470 | } |
471 | # Get the select added by the config file |
472 | my $select = $form->get_element({type => 'Select'}); |
473 | # Add the authors to it |
474 | $select->options(\@authors); |
475 | # Populate the form with existing values from DB |
aee27483 |
476 | $form->model->default_values($book); |
59d6a035 |
477 | } |
478 | |
479 | # Set the template |
766e64ba |
480 | $c->stash(template => 'books/formfu_create.tt2'); |
59d6a035 |
481 | } |
482 | |
16d0d396 |
483 | Most of this code should look familiar to what we used in the |
484 | C<formfu_create> method (in fact, we should probably centralize some of |
59d6a035 |
485 | the common code in separate methods). The main differences are: |
486 | |
487 | =over 4 |
488 | |
489 | =item * |
490 | |
16d0d396 |
491 | We have to manually specify the name of the FormFu .yml file as an |
492 | argument to C<:FormConfig> because the name can no longer be |
ccc9b2bc |
493 | automatically deduced from the name of our action/method (by default, |
494 | FormFu would look for a file named C<books/formfu_edit.yml>). |
59d6a035 |
495 | |
496 | =item * |
497 | |
16d0d396 |
498 | We load the book object from the stash (found using the $id passed to |
ccc9b2bc |
499 | the Chained object method) |
59d6a035 |
500 | |
501 | =item * |
502 | |
ccc9b2bc |
503 | We use C<$id> to look up the existing book from the database. |
504 | |
505 | =item * |
59d6a035 |
506 | |
16d0d396 |
507 | We make sure the book lookup returned a valid book. If not, we set the |
508 | error message and return to the book list. |
2314f851 |
509 | |
59d6a035 |
510 | =item * |
511 | |
16d0d396 |
512 | If the form has been submitted and passes validation, we skip creating a |
513 | new book and just use C<$form-E<gt>model-E<gt>update> to update the |
514 | existing book. |
59d6a035 |
515 | |
516 | =item * |
517 | |
16d0d396 |
518 | If the form is being displayed for the first time (or has failed |
59d6a035 |
519 | validation and it being redisplayed), we use |
16d0d396 |
520 | C<$form-E<gt>model-E<gt>default_values> to populate the form with data |
521 | from the database. |
59d6a035 |
522 | |
523 | =back |
524 | |
16d0d396 |
525 | Then, edit C<root/src/books/list.tt2> and add a new link below the |
526 | existing "Delete" link that allows us to edit/update each existing book. |
527 | The last E<lt>tdE<gt> cell in the book list table should look like the |
59d6a035 |
528 | following: |
529 | |
ab0db558 |
530 | ... |
59d6a035 |
531 | <td> |
532 | [% # Add a link to delete a book %] |
c016427f |
533 | <a href="[% |
534 | c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a> |
59d6a035 |
535 | [% # Add a link to edit a book %] |
c016427f |
536 | <a href="[% |
537 | c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a> |
59d6a035 |
538 | </td> |
ab0db558 |
539 | ... |
540 | |
c016427f |
541 | B<Note:> Only add three lines (the "Add a link to edit a book" comment and |
16d0d396 |
542 | the href for C<formfu_edit>). Make sure you add it below the existing |
543 | C<delete> link. |
59d6a035 |
544 | |
545 | |
546 | =head2 Try Out the Edit/Update Feature |
547 | |
16d0d396 |
548 | Make sure you are still logged in as C<test01> and go to the |
549 | L<http://localhost:3000/books/list> URL in your browser. Click the |
550 | "Edit" link next to "Internetworking with TCP/IP Vol. II", change the |
551 | rating to a 3, the "II" at end of the title to the number "2", add |
552 | Stevens as a co-author (control-click), and click Submit. You will then |
553 | be returned to the book list with a "Book edited" message at the top in |
59d6a035 |
554 | green. Experiment with other edits to various books. |
d682d02d |
555 | |
8a472b34 |
556 | |
5fe0e6dd |
557 | =head2 More Things to Try |
558 | |
559 | You are now armed with enough knowledge to be dangerous. You can keep |
560 | tweaking the example application; some things you might want to do: |
561 | |
562 | =over 4 |
563 | |
564 | =item * |
565 | |
acbd7bdd |
566 | Add an appropriate authorization check to the new Edit function. |
5fe0e6dd |
567 | |
568 | =item * |
569 | |
570 | Cleanup the List page so that the Login link only displays when the user |
571 | isn't logged in and the Logout link only displays when a user is logged |
572 | in. |
573 | |
574 | =item * |
575 | |
576 | Add a more sensible policy for when and how users and admins can do |
577 | things in the CRUD cycle. |
578 | |
579 | =item * |
580 | |
581 | Support the CRUD cycle for authors. |
582 | |
583 | =back |
584 | |
585 | Or you can proceed to write your own application, which is probably the |
586 | real reason you worked through this Tutorial in the first place. |
cca5cd98 |
587 | |
8a472b34 |
588 | |
766e64ba |
589 | =head2 Config::General Config for this tutorial |
1fba2369 |
590 | |
591 | If you are having difficulty with YAML config above, please save the |
592 | below into the file C<formfu_create.conf> and delete the |
16d0d396 |
593 | C<formfu_create.yml> file. The below is in L<Config::General> format |
594 | which follows the syntax of Apache config files. |
1fba2369 |
595 | |
596 | constraints Required |
597 | <elements> |
1fba2369 |
598 | <constraints> |
599 | min 5 |
600 | max 40 |
601 | type Length |
602 | message Length must be between 5 and 40 characters |
603 | </constraints> |
604 | filter TrimEdges |
605 | filter HTMLEscape |
606 | name title |
607 | type Text |
608 | label Title |
609 | <attributes> |
610 | title Enter a book title here |
611 | </attributes> |
612 | </elements> |
613 | <elements> |
1fba2369 |
614 | constraints Integer |
615 | filter TrimEdges |
616 | filter NonNumeric |
617 | name rating |
618 | type Text |
619 | label Rating |
620 | <attributes> |
621 | title Enter a rating between 1 and 5 here |
622 | </attributes> |
623 | </elements> |
624 | <elements> |
625 | constraints Integer |
626 | filter TrimEdges |
627 | filter HTMLEscape |
628 | name authors |
629 | type Select |
630 | label Author |
631 | multiple 1 |
632 | size 3 |
633 | </elements> |
634 | <elements> |
635 | value Submit |
636 | name submit |
637 | type Submit |
638 | </elements> |
639 | indicator submit |
3dba69ab |
640 | |
1fba2369 |
641 | |
cca5cd98 |
642 | =head1 AUTHOR |
643 | |
644 | Kennedy Clark, C<hkclark@gmail.com> |
645 | |
53243324 |
646 | Feel free to contact the author for any errors or suggestions, but the |
647 | best way to report issues is via the CPAN RT Bug system at |
648 | <https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>. |
649 | |
650 | The most recent version of the Catalyst Tutorial can be found at |
59884771 |
651 | L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>. |
cca5cd98 |
652 | |
ec3ef4ad |
653 | Copyright 2006-2010, Kennedy Clark, under the |
654 | Creative Commons Attribution Share-Alike License Version 3.0 |
4768184b |
655 | (L<http://creativecommons.org/licenses/by-sa/3.0/us/>). |