Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Apress.Pro.Drupal.7.Development.3rd.Edition.Dec.2010.pdf
Скачиваний:
73
Добавлен:
14.03.2016
Размер:
12.64 Mб
Скачать

CHAPTER 11 THE FORM API

Note Drupal will prefix the string you give for #theme with theme_, so we set #theme to formexample_coloredfieldset and not theme_formexample_coloredfieldset, even though the name of the theme function that will be called is the latter. See Chapter 9 to learn why this is so.

Specifying Validation and Submission Functions with hook_forms()

Sometimes, you have a special case where you want to have many different forms but only a single validation or submit function. This is called code reuse, and it’s a good idea in that kind of a situation. The node module, for example, runs all kinds of node types through its validation and submission functions. So we need a way to map multiple form IDs to validation and submission functions. Enter hook_forms().

When Drupal is retrieving the form, it first looks for a function that defines the form based on the form ID (in our code, we used the formexample_nameform() function for this purpose). If it doesn’t find that function, it invokes hook_forms(), which queries all modules for a mapping of form IDs to callbacks. For example, node.module uses the following code to map all different kinds of node form IDs to one handler:

/**

* Implements hook_forms(). All node forms share the same form handler. */

function node_forms() { $forms = array();

if ($types = node_get_types()) {

foreach (array_keys($types) as $type) {

$forms[$type .'_node_form']['callback'] = 'node_form';

}

}

return $forms;

}

In our form example, we could implement hook_forms() to map another form ID to our existing code.

/**

* Implements hook_forms(). */

function formexample_forms($form_id, $args) { $forms['formexample_special'] = array(

'callback' => 'formexample_nameform'); return $forms;

}

Now, if we call drupal_get_form('formexample_special'), Drupal will first check for a function named formexample_special() that defines the form. If it cannot find this function, hook_forms() will be called, and Drupal will see that we have mapped the form ID formexample_special to

257

CHAPTER 11 THE FORM API

formexample_nameform. Drupal will call formexample_nameform() to get the form definition, and then attempt to call formexample_special_validate() and formexample_special_submit() for validation and submission, respectively.

Call Order of Theme, Validation, and Submission Functions

As you’ve seen, there are several places to give Drupal information about where your theme, validation, and submission functions are. Having so many options can be confusing, so here’s a summary of where Drupal looks, in order, for a theme function, assuming you are using a theme named mytheme, and you’re calling drupal_get_form('formexample_nameform'). This is, however, dependent upon your hook_theme() implementation.

First, if $form['#theme'] has been set to “foo” in the form definition then the order of checks that Drupal performs is as follows:

1.themes/mytheme/foo.tpl.php // Template file provided by theme.

2.formexample/foo.tpl.php // Template file provided by module.

3.mytheme_foo() // Function provided theme.

4.phptemplate_foo() // Theme function provided by theme engine.

5.theme_foo() // 'theme_' plus the value of $form['#theme'].

However, if $form['#theme'] has not been set in the form definition then the order is:

1.themes/mytheme/formexample-nameform.tpl.php // Template provided by theme.

2.formexample/formexample-nameform.tpl.php // Template file provided by module.

3.mytheme_formexample_nameform() // Theme function provided by theme.

4.phptemplate_formexample_nameform() // Theme function provided by theme engine.

5.theme_formexample_nameform() // 'theme_' plus the form ID.

During form validation, a validator for the form is set in this order:

1.A function defined by $form['#validate']

2.formexample_nameform_validate // Form ID plus 'validate'.

And when it’s time to look for a function to handle form submittal, Drupal looks for the following:

1.A function defined by $form['#submit']

2.formexample_nameform_submit // Form ID plus 'submit'.

Remember that forms can have multiple validation and submission functions.

Writing a Validation Function

Drupal has a built-in mechanism for highlighting form elements that fail validation and displaying an error message to the user. Examine the validation function in our example to see it at work:

258

CHAPTER 11 THE FORM API

/**

* Validate the form. */

function formexample_nameform_validate($form, &$form_state) { if ($form_state['values']['user_name'] == 'King Kong') {

// We notify the form API that this field has failed validation. form_set_error('user_name',

t('King Kong is not allowed to use this form.'));

}

}

Note the use of form_set_error(). When King Kong visits our form and types in his name on his giant gorilla keyboard, he sees an error message at the top of the page, and the field that contains the error has its contents highlighted in red, as shown in Figure 11-6.

Figure 11-6. Validation failures are indicated to the user.

Perhaps he should have used his given name, Kong, instead. Anyway, the point is that form_set_error() files an error against our form and will cause validation to fail.

259

Download from Wow! eBook <www.wowebook.com>

CHAPTER 11 THE FORM API

Validation functions should do just that—validate. They should not, as a general rule, change data. However, they may add information to the $form_state array, as shown in the next section.

If your validation function does a lot of processing and you want to store the result to be used in your submit function, you have two different options. You could use form_set_value() or $form_state.

Using form_set_value() to Pass Data

The most formal option is to create a form element to stash the data when you create your form in your form definition function, and then use form_set_value() to store the data. First, you create a placeholder form element:

$form['my_placeholder'] = array( '#type' => 'value',

'#value' => array()

);

Then, during your validation routine, you store the data:

//Lots of work here to generate $my_data as part of validation.

...

//Now save our work.

form_set_value($form['my_placeholder'], $my_data, &$form_state);

And you can then access the data in your submit function:

//Instead of repeating the work we did in the validation function,

//we can just use the data that we stored.

$my_data = $form_state['values']['my_placeholder'];

Or suppose you need to transform data to a standard representation. For example, you have a list of country codes in the database that you will validate against, but your unreasonable boss insists that users be able to type their country names in text fields. You would need to create a placeholder in your form and validate the user’s input using a variety of trickery so you can recognize both “The Netherlands” and “Nederland” as mapping to the ISO 3166 country code “NL.”

$form['country'] = array( '#title' => t('Country'), '#type' => 'textfield',

'#description' => t('Enter your country.')

);

// Create a placeholder. Will be filled in during validation. $form['country_code'] = array(

'#type' => 'value', '#value' => ''

);

Inside the validation function, you’d save the country code inside the placeholder.

260

CHAPTER 11 THE FORM API

// Find out if we have a match.

$country_code = formexample_find_country_code($form_state['values']['country']); if ($country_code) {

// Found one. Save it so that the submit handler can see it. form_set_value($form['country_code'], $country_code, &$form_state);

}

else {

form_set_error('country', t('Your country was not recognized. Please use a standard name or country code.'));

}

Now, the submit handler can access the country code in $form_values['country_code'].

Using $form_state to Pass Data

A simpler approach is to use $form_state to store the value. Since $form_state is passed to both validation and submission functions by reference, validation functions can store data there for submission functions to see. It is a good idea to use your module’s namespace within $form_state instead of just making up a key.

//Lots of work here to generate $weather_data from slow web service

//as part of validation.

...

// Now save our work in $form_state. $form_state['mymodulename']['weather'] = $weather_data

And you can then access the data in your submit function:

//Instead of repeating the work we did in the validation function,

//we can just use the data that we stored.

$weather_data = $form_state['mymodulename']['weather'];

You may be asking, “Why not store the value in $form_state['values'] along with the rest of the form field values?” That will work too, but keep in mind that $form_state['values'] is the place for form field values, not random data stored by modules. Remember that because Drupal allows any module to attach validation and submission functions to any form, you cannot make the assumption that your module will be the only one working with the form state, and thus data should be stored in a consistent and predictable way.

Element-Specific Validation

Typically, one validation function is used for a form. But it is possible to set validators for individual form elements as well as for the entire form. To do that, set the #element_validate property for the element to an array containing the names of the validation functions. A full copy of the element’s branch of the form data structure will be sent as the first parameter. Here’s a contrived example where we force the user to enter spicy or sweet into a text field:

261

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]