| 7 system.api.php | hook_forms( |
| 5 core.php | hook_forms() |
| 6 core.php | hook_forms($form_id, $args) |
| 8 system.api.php | hook_forms($form_id, $args) |
Map form_ids to builder functions.
This hook allows modules to build multiple forms from a single form "factory" function but each form will have a different form id for submission, validation, theming or alteration by other modules.
The callback arguments will be passed as parameters to the function. Callers of drupal_get_form() are also able to pass in parameters. These will be appended after those specified by hook_forms().
See node_forms() for an actual example of how multiple forms share a common building function.
Parameters
$form_id: The unique string identifying the desired form.
$args: An array containing the original arguments provided to drupal_get_form().
Return value
An array keyed by form id with callbacks and optional, callback arguments.
Related topics
Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.
- node_forms in modules/
node/ node.module - Implementation of hook_forms(). All node forms share the same form handler
- search_forms in modules/
search/ search.module - trigger_forms in modules/
trigger/ trigger.module - Implementation of hook_forms(). We reuse code by using the same assignment form definition for each node-op combination.
- user_forms in modules/
user/ user.module - Implementation of hook_forms().
- drupal_retrieve_form in includes/
form.inc - Retrieves the structured array that defines a given form.
File
- developer/
hooks/ core.php, line 835 - These are the hooks that are invoked by the Drupal core.
Code
function hook_forms($form_id, $args) {
$forms['mymodule_first_form'] = array(
'callback' => 'mymodule_form_builder',
'callback arguments' => array('some parameter'),
);
$forms['mymodule_second_form'] = array(
'callback' => 'mymodule_form_builder',
);
return $forms;
}
Comments
I had some trouble
PermalinkI had some trouble implementing hook_forms at first, but after some tinkering this is what I have worked out as a solution to printing multiple versions of the same form on one page:
<?phpfunction mymodule_form($form_state, $args){…}
?>
<?php//normal call to get form, but append a key (of your own creation, e.g. a nid, or a static counting variable) to the end of the argument.
drupal_get_form('mymodule_form_'.$key)
?>
<?php function mymodule_forms($form_id,$args){//Check if the form_id passed to drupal_get_form() contains the string 'mymodule_form'
if (strpos($form_id, 'mymodule_form') !== FALSE) {
//Lets attach mymodule_form to $forms[$form_id]. This effectively allows you to use the same form builder function to build a form with any form_id of your choice.
$forms[$form_id] = array(
'callback' => 'mymodule_form',
);
return $forms;
}
}
?>
<?phpfunction mymodule_form($form_state, $args){
//this is the function that will get called on form submission.
$form['#submit'][] = 'mymodule_form_submit';
}
?>
Thank you!
Permalink#2 save my life!
I would have been stack in here if it wasn't for you! Thank you!
Another Example Implementation
PermalinkGreetings,
Here is another example of a hook_forms implementation, using a more-or-less generic and reusable approach. One benefit of this method is that one has to go to fewer places to update form generation, validation, and submit code when dealing with multiple forms. I know this is a lengthy post, but this hook_forms process is somewhat non-intuitive, so I'm trying to give a complete (if silly) example for the sake of clarity.
hook forms
First set up a generic catch-all for your forms. In this case, I'm assuming that any $form_id value which starts with "mymodule_" will be a form generated by this module's code. In practice, you may want to narrow this down a little more, depending on the scope of your module's deployment, the uniqueness of your module's name (i.e. the collision probability), etc.
<?php
/**
* Implementation of hook_forms
*
* @see http://api.drupal.org/api/drupal/developer--hooks--core.php/function/hook_forms/6
*
* @param $form_id array
* String representing the name of the form itself. Typically this is the
* name of the function that generated the form.
* @param $args array
* An array containing the original arguments provided to drupal_get_form().
*
* @return array
* An array keyed by form id with callbacks and optional, callback arguments.
*/
function mymodule_forms($form_id, $args) {
$forms = array();
// if the name of this form does not start with the name of// the module, then do nothing here
if (0 !== strpos($form_id, 'mymodule_')) {return $forms;}
// the requested form belong to this module, so provide the
// information necessary to for drupal to call the function that
// builds this form
$forms[$form_id] = array(
'callback' => 'mymodule_form_builder',
'callback arguments' => array($form_id, $args,)
);
return $forms;
}
?>
form builder
Next, build the form-array definitions (ref http://api.drupal.org/api/drupal/modules--node--node.module/function/nod...). Note that the $form_state var will likely be empty at this point, but can hold useful data if this form is being re-generated.
<?php
/**
* Renders a module's form.
*
* @return array
* The form array defind the form form for the given form_id
*/
function mymodule_form_builder(&$form_state, $form_id, $args) {
$form = array();
switch($form_id) {
case 'mymodule_special_form_one':
$form['title'] = array(
'#type' => 'textfield',
'#title' => t('Subject'),
'#default_value' => 'Oh Hie',
'#size' => 60,
'#maxlength' => 128,
'#required' => TRUE,
);
$form['mything_nid'] = array('#type' => 'value',
'#value' => $args[0],
);
$form['mything_uid'] = array(
'#type' => 'value',
'#value' => $args[1],
);
$form['mything_submit'] = array(
'#type' => 'submit',
'#value' => t('Do It'),
'#weight' => 10,
);
break;
case 'mymodule_special_form_two':
$form['foo'] = array(
'#type' => 'select',
'#title' => t('Bar'),
'#default_value' => array('teaser'),
'#options' => array(
'title' => t('Titles only'),
'teaser' => t('Titles plus teaser'),
'fulltext' => t('Full text'),
),
);
$form['mything_submit'] = array(
'#type' => 'submit',
'#value' => t('Make It So'),
'#weight' => 10,
);
break;
default:
$form['unknown'] = array(
'#type' => 'markup',
'#value' => '<div>'.t('Unknown form type, "%form_id%", requested in function mymodule_form_builder.', array('%form_id%'=>$form_id)).'</div>',
);
break;
}
// all our forms go through the validate and submit processes we have set up for this module's use$form['#validate'][] = 'mymodule_form_validate';
$form['#submit'][] = 'mymodule_form_submit';
return $form;
}
?>
form validation
Perform whatever validation you deem appropriate for each form.
<?php
/**
* Validates a mymodule form.
*
* @param $form array
* array of form elements that comprise the form.
* @param $form_state array
* A keyed array containing the current state of the form.
*
* @return void
*/
function mymodule_form_validate($form, &$form_state) {
switch($form_state['values']['form_id']) {
case 'mymodule_special_form_one':
if (!$form_state['values']['title']) {
form_set_error('title', t('No can haz empty title!'));
}
break;
case 'mymodule_special_form_two':
// Do nothing - we blindly trust this set of values! o.O
break;
default:
// does your module need to validate something for every form it
// owns? do so here...
break;
}
}
?>
form submit / save
Validation has passed, so save the form values.
<?php
/**
* Processes a mymodule form.
*
* @param $form array
* array of form elements that comprise the form.
* @param $form_state array
* A keyed array containing the current state of the form.
*
* @return void
*/
function mymodule_form_submit($form, &$form_state) {
switch($form_state['values']['form_id']) {
case 'mymodule_special_form_one':
// save 'title', 'nid', and 'uid' to the database
case 'mymodule_special_form_two':
// save 'foo' to the database
break;
default:
break;
}
}
?>
example usage
An example call to the new form might look something like this:
<?php$my_form = drupal_get_form('mymodule_special_form_one', $nid, $uid);
?>
Or in a menu, like this:
<?php
/**
* Implementation of hook_menu
*
* @see http://api.drupal.org/api/function/hook_menu/6
*
* @return array
* array of menu definitions key by url path
*/
function mymodule_menu() {
$items = array();
$items['mymodule/stuff'] = array(
'title' => 'Foo',
'description' => 'Foo Bar Sna Foo',
'page callback' => 'drupal_get_form',
'page arguments' => array('mymodule_special_form_two'),
'access callback' => 'user_is_logged_in',
'type'=>MENU_NORMAL_ITEM,
);
return $items;
}
?>
A note about your examples
PermalinkThanks for your example. I followed it and now I have my forms working on the same page.
I just want to mention a couple of issues that I had that may help others:
Apart from those two things, everything worked fine.
Is there a problem with invoking hooks_forms twice in a module?
PermalinkHi, I found this article really helpful when I needed to create multiple forms on a single page with different IDs so that submitting one didn't trigger the others to submit. But when I attempted to invoke the hook again in the same module I could not get the second function to run.
My code below is almost identical to similiar functions declared earlier in my module for a different page (chic_request_status_page) and the work fine. But in this second declaration my function chic_bookings_listing_update_forms is never entered. and I get all the forms with the correct IDs but there are no select or input elements from my chic_bookings_listing_update_form_builder function.
Hope this makes sense to someone,
-johnny;j
<?php/**
* Now display the booking listing page with the form to update the status
**/
function chic_bookings_listing_page(){
//getting the hotel node id for the query from the url
$hotelNodeId = arg(3);
// run the query to get the list.
$query = "SELECT DATE_FORMAT(V.createDate,GET_FORMAT(DATE,'JIS')) AS createDate, DATE_FORMAT(V.fromDate,GET_FORMAT(DATE,'JIS')) AS fromDate, DATE_FORMAT(V.toDate,GET_FORMAT(DATE,'JIS')) AS toDate, firstname, lastname, CustomerEMail, HotelName, field_status_value, CRHotelNodeId, field_rates_from_currency, field_booking_amount_value, BookingNodeID FROM vw_CR_Requests_BookingStatus V WHERE (field_status_value is NULL or field_status_value = 'pending') AND CRHotelNodeId = %d";
// Put together the query and identifier
$queryResource = db_query($query, $hotelNodeId);
// Retreive the resource as an array
$rsArray = array();
while( $row = db_fetch_object($queryResource)){
$rsArray[] = $row;
}
// Build out the page
$booking_list ="<h3 class='title'>Please update the status of these bookings.</h3>";
$booking_list .= "<table class='views-table view-hotel-bookings-listing'><thead>
<tr><th>Date</th><th>From</th><th>To</th>
<th>Firstname</th><th>Lastname</th><th>Email</th><th>Status</th><th>Update</th></tr>
</thead><tbody>";
foreach($rsArray as $rd){
$booking_list .="<tr class='even'><td>".$rd->createDate."</td>"
."<td>".$rd->fromDate."</td>"
."<td>".$rd->toDate."</td>"
."<td>".$rd->firstname."</td>"
."<td>".$rd->lastname."</td>"
."<td>".$rd->CustomerEMail."</td>"
."<td>".$rd->field_status_value."</td>"
."<td>".drupal_get_form('chic_bookings_listing_update_form_'.$rd->BookingNodeID, $rd)."</td>"
."</tr>";
}
$booking_list .="</tbody></table>";
return $booking_list;
}// chic_booking_listing_page function chic_bookings_listing_update_forms($form_id, $status){
$forms = array();
firep($forms);
if(0 !== strpos($form_id, "chic_bookings_listing_update_")){
return $forms;
}
$forms[$form_id] = array(
"callback" => "chic_bookings_listing_update_form_builder",
"callback arguments" => array($form_id, $status,)
);
return $forms;
} /**
* Implementation of hook_form() to add the update form to the booking listing page
*/
function chic_bookings_listing_update_form_builder(&$form_state, $form_id, $status) {
firep($status, "vw_CR_Requests_BookingStatus");
static $js_added = FALSE;
if (!$js_added) {
$js_added = TRUE;
drupal_add_js(drupal_get_path('module', 'chic_request_status') . '/bookings_edit.js');
}
$currency = check_plain($status->field_rates_from_currency);
$booking_amount = $status->field_booking_amount_value;
$form = array();
$form['status'] = array('#type' => 'select',
'#options' => array(
0 => t('Change status'),
BOOKING_STATUS_STAYED => t('Stayed'),
BOOKING_STATUS_DENIED => t('Denied'),
),
'#attributes' => array('class' => 'booking-status-select'),
);
$form['amount'] = array('#type' => 'textfield',
'#title' => t('Amount [@currency]', array('@currency' => $currency)),
'#size' => 3,
'#attributes' => array('class' => 'booking-amount-value'),
);
$form['nid'] = array('#type' => 'hidden',
'#value' => intval($status->BookingNodeID),
);
$form['submit'] = array('#type' => 'submit',
'#value' => t('Update'),
);
return $form;
}
function
chic_bookings_listing_update_form_validate($form, &$form_state){if (empty($form_state['values']['nid'])) {
form_set_error('', t('Could not find nid value.'));
}
$status = $form_state['values']['status'];
if (empty($status)) {
form_set_error('status', t('Please select a status'));
}
if (
$status != BOOKING_STATUS_STAYED) {return;
}
$amount = $form_state['values']['amount'];if (empty($amount) ){
form_set_error('amount', t('Please enter an amount value'));
}
if(! is_numeric($amount)) {
form_set_error('amount', t('Please enter an numeric amount value'));
}
}//chic_bookings_listing_update_form_validate function chic_bookings_listing_update_form_submit($form, &$form_state){
$amount = $form_state['values']['amount'];
$status = check_plain($form_state['values']['status']);
$nid = $form_state['values']['nid'];
$booking_node = node_load($nid);
if (
$status == BOOKING_STATUS_STAYED) {$booking_node->field_booking_amount = array(array('value' => $amount));
$booking_node->field_status = array(array('value' => $status));
node_save($booking_node);
} elseif ($status == BOOKING_STATUS_DENIED) {
$booking_node->field_booking_amount = array(array('value' => 0));
$booking_node->field_status = array(array('value' => $status));
node_save($booking_node);
}
drupal_set_message(t('Your booking was updated.'));}//chic_bookings_listing_update_submit
?>