<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=us-ascii">
<TITLE>Message</TITLE>
<META content="MSHTML 6.00.2900.2963" name=GENERATOR></HEAD>
<BODY>
<DIV><FONT face=Verdana size=2>Greetings, folks! There's been talk for the past
week or so about the 'pull model' patch for Forms API that chx rolled. It's been
committed after some furious rounds of polishing, and it removes many barriers
for advanced use of the Forms API. It DOES, however, require some
conversion/updates to existing functions that generate and display forms. Here's
the quick and easy guide to the changes.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>1) What changed and why?<BR>Previously, Forms API
used a 'push model.' You built an array of form elements, then called
drupal_get_form($form_id, $form) to 'push' it into the API for processing and
rendering. This worked OK for many situations, but also made it terribly
cumbersome for programmatically submitting forms (for import/export). It also
made workflow-oriented systems that chained multiple forms together a lot more
hacky and kludgy than necessary.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>Now, the Forms API uses a 'pull model.' You call
drupal_get_form($form_id), and it 'pulls' the form from a builder function, then
processes and renders it as appropriate. That means that existing modules need
to separate their form-building code from their general page and content
rendering code -- most already do this, but it's an important distinction, and
making it 'official' allows the system to treat forms more
consistently.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>2) So, how do I convert my code?<BR>By default,
Forms API will check for a function that shares the same name as the form's ID.
For example:</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2><?php<BR>function
mymodule_edit_record($record) {<BR> $output = 'This is my edit
page!';<BR> $form['my_field'] = array(<BR> '#type' =>
'textfield',<BR> '#title' => 'Record
name',<BR> '#default_value' => $record->name,<BR>
);<BR> $output .= drupal_get_form('record_edit_form', $form);<BR>
return $output;<BR>}<BR>?></FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>Would need to change to the
following:</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2><?php<BR>function
mymodule_edit_record($record) {<BR> $output = 'This is my edit
page!';<BR> $output .= drupal_get_form('mymodule_edit_record_form',
$record);<BR> return $output;<BR>}</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>function mymodule_edit_record_form($record)
{<BR> $form['my_field'] = array(<BR> '#type' =>
'textfield',<BR> '#title' => 'Record
name',<BR> '#default_value' => $record->name,<BR>
);<BR> return $form;<BR>}<BR>?></FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>Once that 'builder' function is defined, you can
call drupal_get_form('mymodule_edit_record_form', $record) anywhere in your
code, and the API will handle retrieving it. In fact, if your form page was
simple and just consisted of building a form, then returning the results of
drupal_get_form(), you can now use the following trick when you define the menus
for your module:</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2><?php<BR>function mymodule_menu($may_cache =
TRUE) {<BR> $items[] = array(<BR> 'path' =>
'mymodule/form-page',<BR> 'title' => t('edit a
record'),<BR> 'callback' =>
'drupal_get_form',<BR> 'callback arguments' =>
array('mymodule_form_builder')<BR> 'type' =>
MENU_LOCAL_TASK<BR> );<BR> return $items;<BR>?></FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>In the above example, instead of putting a custom
page-generation function in as your callback, and adding a single
drupal_get_form($form_id) call as its body, you can just tell the menu system to
call drupal_get_form() with your form ID.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>3) What if I have several forms that use
different IDs, but share the same layout, submit and validate functions, and so
on?<BR>We're glad you asked. Modules can now implement hook_forms() to handle
complex mapping of form IDs to builder functions. It's how node.module maps the
id for each node type editing form to the central form-building function for the
node edit screen. Here's an example:</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2><?php<BR>function mymodule_hook_forms()
{<BR> $forms['mymodule_first_form'] = array(<BR>
'callback' => 'mymodule_form_builder',<BR> 'callback
arguments' => array('some parameter'),<BR> );<BR>
$forms['mymodule_second_form'] = array(<BR> 'callback' =>
'mymodule_form_builder',<BR> 'callback arguments' =>
array('a different parameter'),<BR> );<BR> return
$forms;<BR>}</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>function mymodule_first_page() {<BR> return
drupal_get_form('mymodule_first_form');<BR>}</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>function mymodule_second_page() {<BR>
return drupal_get_form('mymodule_second_form');<BR>}</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>function mymodule_form_builder($param)
{<BR> $form = array()<BR> // build the form here</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2> if ($param == 'some parameter')
{<BR> // Add another field, change a default
value...<BR> }<BR> <BR> // This is used the way $callback was
in 4.7 Forms API: it is used as the prefix for<BR> // _submit() and
_validate() functions to process the form.<BR> $form['#base'] =
'mymodule_form';<BR> <BR> return $form;<BR>}<BR>?></FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>In the above code, the mymodule_first_page() and
mymodule_second_page() functions display slight variations on the same form.
Because no functions named 'mymodule_first_form' or 'mymodule_second_form'
exist, Forms API looks to hook_forms() to find the builder function. Each entry
in the array returned by hook_forms() can also define callback arguments that
will be passed to the builder function.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>In the mymodule_form_builder() function, the use
of $form['#base'] is also important to note. It's a lot like the little-used but
helpful $callback parameter that was used in the 4.7 version of
drupal_get_form(). If $form['#base'] is set, its value will be used to look up
the proper submit, validate, and theme functions for the form rather than the
form's ID.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>In the future, hook_forms() will also allow us to
capture other meta data about forms like access restrictions, workflow
information, and so on. For now, though, the callback and optional callback
arguments are all you need to worry about if you use it.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>4) OK, that's great. How do I programmatically
submit form data, then?<BR>Aha, the meat of it. Now, it's possible to completely
circumvent drupal_get_form() and all of its submission, rendering, and
redirection logic. You just pull up a form using its ID, and populate the new
$form['#post']['edit'] element with the form values you'd like to submit. Then
call drupal_process_form(). For example:</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2><?php<BR>function mymodule_create_user()
{<BR> // register a new user<BR> $form =
drupal_retrieve_form('user_register');<BR> $form['#post']['edit']['name']
= 'robo-user';<BR> $form['#post']['edit']['mail'] = <A
href="mailto:'robouser@example.com'">'robouser@example.com'</A>;<BR>
$form['#post']['edit']['pass'] = 'password';<BR>
drupal_process_form('user_register', $form);<BR> <BR> if
(form_get_errors()) {<BR> // whoops, something went
wrong!<BR> }<BR>}<BR>?></FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>And that, dear friends, is about it. To
recap:</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>1) drupal_get_form() now takes a $form_id, NOT a
$form_id and a $form.<BR>2) You need a builder function with the name of the
$form_id that returns the fully constructed $form array<BR>3) If your page
content is JUST a form, you can use drupal_get_form as your menu callback, with
$form_id as your menu callback argument.<BR>4) If you need to use the same $form
for multiple $form_ids, use hook_forms()<BR>5) If you want the submit, validate,
and theme functions to use naming different than the $form_id, set
$form['#base']<BR>6) Use drupal_retrieve_form(), $form['#post']['edit'], and
drupal_process_form() to programmatically submit forms.</FONT></DIV>
<DIV> </DIV>
<DIV><FONT face=Verdana size=2>Have fun!</FONT></DIV></BODY></HTML>