Commit | Line | Data |
b80f58e5 |
1 | =head1 NAME |
2 | |
3 | Catalyst::Manual::Tutorial::AdvancedCRUD::FormFu - Catalyst Tutorial - Part 9: Advanced CRUD - FormFu |
4 | |
5 | |
c010ae0d |
6 | NOTE: This part of the tutorial is in progress and will be ready soon. |
7 | |
b80f58e5 |
8 | =head1 OVERVIEW |
9 | |
10 | This is B<Part 9 of 10> for the Catalyst tutorial. |
11 | |
12 | L<Tutorial Overview|Catalyst::Manual::Tutorial> |
13 | |
14 | =over 4 |
15 | |
16 | =item 1 |
17 | |
18 | L<Introduction|Catalyst::Manual::Tutorial::Intro> |
19 | |
20 | =item 2 |
21 | |
22 | L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics> |
23 | |
24 | =item 3 |
25 | |
26 | L<More Catalyst Basics|Catalyst::Manual::Tutorial::MoreCatalystBasics> |
27 | |
28 | =item 4 |
29 | |
30 | L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD> |
31 | |
32 | =item 5 |
33 | |
34 | L<Authentication|Catalyst::Manual::Tutorial::Authentication> |
35 | |
36 | =item 6 |
37 | |
38 | L<Authorization|Catalyst::Manual::Tutorial::Authorization> |
39 | |
40 | =item 7 |
41 | |
42 | L<Debugging|Catalyst::Manual::Tutorial::Debugging> |
43 | |
44 | =item 8 |
45 | |
46 | L<Testing|Catalyst::Manual::Tutorial::Testing> |
47 | |
48 | =item 9 |
49 | |
50 | B<Advanced CRUD> |
51 | |
52 | =item 10 |
53 | |
54 | L<Appendices|Catalyst::Manual::Tutorial::Appendices> |
55 | |
56 | =back |
57 | |
58 | |
59 | =head1 DESCRIPTION |
60 | |
cca5cd98 |
61 | This portion of the tutorial explores L<HTML::FormFu|HTML::FormFu> and |
62 | how it can be used to manage forms, perform validation of form input, |
63 | as well as save and restore data to/from the database. |
64 | |
65 | See |
66 | L<Catalyst::Manual::Tutorial::AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD> |
67 | for additional form management options other than |
68 | L<HTML::FormFu|HTML::FormFu>. |
69 | |
70 | |
71 | =head1 Install C<HTML::FormFu> |
72 | |
73 | If you are following along in Ubuntu, it turns out that C<HTML::FormFu> |
74 | is not yet available as a package at the time this was written. To |
75 | install it with a combination of C<apt-get> packages and traditional |
76 | CPAN modules, first use C<apt-get> to install most of the modules |
77 | required by C<HTML::FormFu>: |
78 | |
79 | sudo apt-get install libtest-nowarnings-perl libdatetime-format-builder-perl \ |
80 | libdatetime-format-strptime-perl libdatetime-locale-perl \ |
81 | libhtml-tokeparser-simple-perl liblist-moreutils-perl \ |
82 | libregexp-copy-perl libregexp-common-perl libyaml-syck-perl libparams-util-perl |
83 | |
84 | Then use the following command to install directly from CPAN the modules |
85 | that aren't available as Ubuntu/Debian packages via C<apt-get>: |
86 | |
87 | sudo cpan File::ShareDir Task::Weaken Config::Any HTML::FormFu \ |
88 | Catalyst::Controller::HTML::FormFu |
89 | |
90 | |
91 | =head1 C<HTML::FormFu> FORM CREATION |
92 | |
93 | This section looks at how L<HTML::FormFu|HTML::FormFu> can be used to |
94 | add additional functionality to the manually created form from Part 4. |
95 | |
96 | |
97 | =head2 Inherit From C<Catalyst::Controller::HTML::FormFu> |
98 | |
99 | First, change your C<lib/MyApp/Controller/Books.pm> to inherit from |
100 | L<Catalyst::Controller::HTML::FormFu|Catalyst::Controller::HTML::FormFu> |
101 | by changing the C<use base> line from the default of: |
102 | |
103 | use base 'Catalyst::Controller'; |
104 | |
105 | to use the FormFu base controller class: |
106 | |
107 | use base 'Catalyst::Controller::HTML::FormFu'; |
108 | |
109 | |
110 | =head2 Add Action to Display and Save the Form |
111 | |
112 | Open C<lib/MyApp/Controller/Books.pm> in your editor and add the |
113 | following method: |
114 | |
115 | =head2 fu_form_create |
116 | |
117 | Build an HTML::FormFu form for book creation and updates |
118 | |
119 | =cut |
120 | |
121 | sub fu_form_create :Local :FormConfig { |
122 | my ($self, $c) = @_; |
123 | |
124 | # Get the form that the :FormConfig attribute saved in the stash |
125 | my $form = $c->stash->{form}; |
126 | |
127 | # Check if the form as been submitted (vs. displaying the initial |
128 | # form) and if the data based validation. "submitted_and_valid" |
129 | # is shorthand for "$form->submitted && !$form->has_errors" |
130 | if ($form->submitted_and_valid) { |
131 | # Create a new book |
132 | my $book = $c->model('DB::Books')->new_result({}); |
133 | # Save the form data for the book |
134 | $form->save_to_model($book); |
135 | # Set a status message for the user |
136 | $c->flash->{status_msg} = 'Book created'; |
137 | # Return to the books list |
138 | $c->response->redirect($c->uri_for('list')); |
139 | $c->detach; |
9cb64a37 |
140 | } else { |
141 | # Get the authors from the DB |
142 | my @authorObjs = $c->model("DB::Authors")->all(); |
143 | # Create an array of arrayrefs where each arrayref is an author |
144 | my @authors; |
145 | foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) { |
146 | push(@authors, [$_->id, $_->last_name]); |
147 | } |
148 | # Get the select added by the config file |
149 | my $select = $form->get_element({type => 'Select'}); |
150 | # Add the authors to it |
151 | $select->options(\@authors); |
152 | } |
153 | |
cca5cd98 |
154 | # Set the template |
155 | $c->stash->{template} = 'books/fu_form_create.tt2'; |
156 | } |
157 | |
158 | |
159 | =head2 Create a Form Config File |
160 | |
161 | Although C<HTML::FormFu> supports any configuration file handled by |
162 | L<Config::Any|Config::Any>, most people tend to use YAML. First |
163 | create a directory to hold your form configuration files: |
164 | |
165 | mkdir -p root/forms/books |
166 | |
167 | Then create the file C<root/forms/books/fu_form_create.yml> and enter the |
168 | following text: |
169 | |
170 | --- |
9cb64a37 |
171 | # indicator is the field that is used to test for form submission |
cca5cd98 |
172 | indicator: submit |
9cb64a37 |
173 | # Start listing the form elements |
cca5cd98 |
174 | elements: |
9cb64a37 |
175 | # The first element will be a text field for the title |
cca5cd98 |
176 | - type: Text |
177 | name: title |
178 | label: Title |
9cb64a37 |
179 | # This is an optional 'mouse over' title pop-up |
cca5cd98 |
180 | attributes: |
181 | title: Enter a book title here |
182 | - type: Text |
183 | name: rating |
184 | label: Rating |
185 | attributes: |
186 | title: Enter a rating between 1 and 5 here |
9cb64a37 |
187 | # The submit button |
cca5cd98 |
188 | - type: Submit |
189 | name: submit |
190 | value: Submit |
191 | |
192 | |
193 | =head2 Update the CSS |
194 | |
195 | Edit C<root/src/ttsite.css> and add the following lines to the bottom of |
196 | the file: |
197 | |
198 | label { |
199 | display: block; |
200 | } |
201 | .submit { |
202 | display: block; |
203 | } |
204 | .error_messages { |
205 | color: [% site.col.error %]; |
206 | } |
207 | |
208 | These changes will display form elements vertically and also show error |
209 | messages in red. Note that we are pulling the color scheme settings |
210 | from the C<root/lib/config/col> file that was created by the TTSite |
211 | helper. This allows us to change the color used by various error styles |
212 | in the CSS from a single location. |
213 | |
214 | |
215 | =head2 Create a Template Page To Display The Form |
216 | |
217 | Open C<root/src/books/fu_form_create.tt2> in your editor and enter the following: |
218 | |
219 | [% META title = 'Create/Update Book' %] |
220 | |
221 | [%# Render the HTML::FormFu Form %] |
222 | [% form %] |
223 | |
224 | <p><a href="[% Catalyst.uri_for('list') %]">Return to book list</a></p> |
225 | |
226 | |
227 | =head2 Add Links for Create and Update via C<HTML::FormFu> |
228 | |
229 | Open C<root/src/books/list.tt2> in your editor and add the following to |
230 | the bottom of the existing file: |
231 | |
232 | <p> |
233 | HTML::FormFu: |
234 | <a href="[% Catalyst.uri_for('fu_form_create') %]">Create</a> |
235 | </p> |
236 | |
237 | |
238 | =head2 Test The <HTML::Widget> Create Form |
239 | |
240 | Press C<Ctrl-C> to kill the previous server instance (if it's still |
241 | running) and restart it: |
242 | |
243 | $ script/myapp_server.pl |
244 | |
245 | Login as C<test01>. Once at the Book List page, click the HTML::FormFu |
246 | "Create" link to display for form produced by C<make_book_widget>. Fill |
247 | out the form with the following values: Title = "Internetworking with |
248 | TCP/IP Vol. II", Rating = "4", and Author = "Comer". Click Submit, and |
249 | you will be returned to the Book List page with a "Book created" status |
250 | message displayed. |
251 | |
252 | Also note that this implementation allows you to can create books with |
253 | bogus information. Although we have constrained the authors with the |
254 | drop-down list, there are no restrictions on items such as the length of |
255 | the title (for example, you can create a one-letter title) and value for |
256 | the rating (you can use any number you want, and even non-numeric values |
257 | with SQLite). The next section will address this concern. |
258 | |
259 | B<Note:> Depending on the database you are using and how you established |
260 | the columns in your tables, the database could obviously provide various |
261 | levels of "type enforcement" on your data. The key point being made in |
262 | the previous paragraph is that the I<web application> itself is not |
263 | performing any validation. |
264 | |
265 | |
266 | =head1 C<HTML::FormFu> VALIDATION AND FILTERING |
267 | |
268 | Although the use of L<HTML::FormFu|HTML::FormFu> in the previous section |
269 | did provide an automated mechanism to build the form, the real power of |
270 | this module stems from functionality that can automatically validate and |
271 | filter the user input. Validation uses constraints to be sure that |
272 | users input appropriate data (for example, that the email field of a |
273 | form contains a valid email address). Filtering can be used to remove |
274 | extraneous whitespace from fields or to escape meta-characters in user |
275 | input. |
276 | |
277 | =head2 Add Constraints |
278 | |
279 | |
280 | Open C<root/forms/books/fu_form_create.yml> in your editor and update it |
281 | to match: |
282 | |
283 | --- |
9cb64a37 |
284 | # indicator is the field that is used to test for form submission |
cca5cd98 |
285 | indicator: submit |
9cb64a37 |
286 | # Start listing the form elements |
cca5cd98 |
287 | elements: |
9cb64a37 |
288 | # The first element will be a text field for the title |
cca5cd98 |
289 | - type: Text |
290 | name: title |
291 | label: Title |
9cb64a37 |
292 | # This is an optional 'mouse over' title pop-up |
cca5cd98 |
293 | attributes: |
294 | title: Enter a book title here |
9cb64a37 |
295 | # Add constraints for the field |
cca5cd98 |
296 | constraints: |
9cb64a37 |
297 | # The user cannot leave this field blank |
cca5cd98 |
298 | - Required |
9cb64a37 |
299 | # Force the length to be between 2 and 30 chars |
cca5cd98 |
300 | - type: Length |
301 | min: 2 |
302 | max: 30 |
9cb64a37 |
303 | # Override the default of 'Invalid input' |
cca5cd98 |
304 | message: Length must be between 2 and 30 characters |
305 | - type: Text |
306 | name: rating |
307 | label: Rating |
308 | attributes: |
309 | title: Enter a rating between 1 and 5 here |
310 | constraints: |
311 | - Required |
9cb64a37 |
312 | # Make sure it's a number |
cca5cd98 |
313 | - Integer |
9cb64a37 |
314 | # The submit button |
cca5cd98 |
315 | - type: Submit |
316 | name: submit |
317 | value: Submit |
9cb64a37 |
318 | # Globally ensure that each field only specified one value |
cca5cd98 |
319 | constraints: |
320 | - SingleValue |
321 | |
322 | ... |
323 | |
324 | =head1 AUTHOR |
325 | |
326 | Kennedy Clark, C<hkclark@gmail.com> |
327 | |
328 | Please report any errors, issues or suggestions to the author. The |
329 | most recent version of the Catalyst Tutorial can be found at |
330 | L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Manual/lib/Catalyst/Manual/Tutorial/>. |
331 | |
332 | Copyright 2006, Kennedy Clark, under Creative Commons License |
333 | (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>). |
334 | |