10 шагов к постижению форм в Друпале
Статья эвакуирована с DrupalDance.com
Этот урок создан специально для начинающих и средне-продвинутых Друпал-разработчиков. Он должен быстро дать понятие об азах Forms API, а также показать возможность создаия более сложных вещей на примере пошаговых форм (№8).
Когда я только начинал подготовку этого урока, у меня был соблазн поставить под каждым куском кода ссылку для скачивания готового примера, но в послествии, я отказался от этого. Будет намного полезнее, если вы сами будете вставлять код в свои модули, тестируя и набираясь опыта в реальных условиях.
И прежде чем начать, я расскажу вам как все-таки заставить любой из этих кусков кода работать. Предположим, вы уже имеете установленный тестовый сайт на Друпал 6. Вам прийдется проделать следующие действия:
- Создать новую директорию в sites/all/modules, например my_module
- Создать файл my_module.info в директории my_module, содержащий это:
- Создать файл my_module.module. Полностью скопировать отсюда первый пример и вставить в my_module.module.
- Включить модуль "My module" на странице модулей (admin/build/modules).
- Перейти на страницу my_module/form для запуска кода.
- Далее вам предстоит провести для каждого примера, полную замену содержимого my_module.module на код последующего примера. Не забывайте после этого переходить на страницу my_module/form для того, чтобы увидеть результаты своей работы.
name = My module
description = Module for form api tutorial
core = 6.x
Пример №1:
Начнем с самой простой формы:
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
// Эта функция вызывается каждый раз, когда мы посещаем страницу 'my_module/form'.
// Функция генерирует и возвращает нашу форму.
function my_module_form() {
// Форма конструируется при помощи функции drupal_get_form(),
// в которую нам нужно передать название "функции-строителя" формы.
return drupal_get_form('my_module_my_form');
}
// Функция-строитель нашей формы.
// Notice it takes one argument, the $form_state
function my_module_my_form($form_state) {
// Наш первый элемент формы — тестовое поле с заголовком "Name".
// Обратите внимание, что 'Name' обернуто в функцию t(). Это
// обеспечит дальнейший перевод слова 'Name', чтобы, например,
// при включенной русской локализации поле называлось 'Имя'.
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
);
return $form;
}
Пример №2:
Делаем форму чуть более полезней, добавив кнопку отправки формы.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
);
// Добавим в форму простую кнопку отправки. Обратите внимание на то,
// что при нажатии на кнопку, вы вернетесь обратно на форму, а все ее
// поля будут очищены. Это стандартное поведение форм.
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
Пример №3:
Демонстрация набора полей (Fieldsets).
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
// Мы создаем элемент "набор полей" и помещаем в него два текстовых
// поля — для имени, фамилии и отчества.
//
// При внимательном рассмотрении этого кода, вы можете заметить, что имя, фамилия
// и отчество объявляются как под-масссивы внутри $form['name']. Это говорит друпалу,
// что эти элементы нужно поместить внутрь набора полей 'Name'.
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
Пример №4:
Демонстрация распахивающегося набора полей и базовой валидации обязательных полей.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
// Делаем набор полей распахивающимся
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE, // распахивающийся
'#collapsed' => FALSE, // и не схопнутый по-умолчанию
);
// Делаем эти поля обязательными
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE, // добавлено обязательное заполнение
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#required' => TRUE, // добавлено обязательное заполнение
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE, // добавлено обязательное заполнение
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
Пример №5:
Демонстрация добавления дополнительных аттрибутов к элементам формы.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
// Демонстрация дополнительных аттрибутов текстовых полей.
//
// Полный список элементов форм и их аттрибутов можно глянуть здесь:
// http://api.drupal.ru/api/file/developer/topics/forms_api_reference.html
//
// Обратите внимание, что в аттрибутах типа #description следует
// стараться использовать английские значения, обернутые в t().
// Это облегчит вам дальнейшую жизнь, если в иной день вы
// захотите сделать многоязычную версию сайта. Если же вы уверены,
// что локализации не будет, или ваш английский оставляет желать лучшего,
// совсем не запрещено указывать там значения прямо на русском.
// Однако, в этом случае КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО оборачивать их в t().
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE,
'#default_value' => t("First name"), // добавлено значение по-умолчанию
'#description' => t("Please enter your first name."), // добавлена подпись
'#size' => 20, // добавлена ширина поля
'#maxlength' => 20, // добавлена максимальная длина строки ввода
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#required' => TRUE,
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE,
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
Пример №6:
Добавление нового элемента и функции валидации формы.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE,
'#default_value' => t("First name"),
'#description' => t("Please enter your first name."),
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#required' => TRUE,
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE,
);
// Новое поле — год рождения. Мы произведем проверку значения
// этого поля в функции валидации формы.
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
// Добавляем функцию валидации формы. В ней мы будем проверять
// значение поля "год рождения", чтобы быть уверенными, что оно
// находится между 1900 и 2000. Если нет, будет выбрасываться ошибка.
//
// Обратите внимание на название функции. Это просто название
// функции-строителя формы с '_validate' на конце. Названная таким
// образом функция, будет служить валидатором формы.
function my_module_my_form_validate($form, &$form_state) {
$year_of_birth = $form_state['values']['year_of_birth'];
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
}
Пример №7:
Добавление функции-обработчика формы.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#required' => TRUE,
'#default_value' => t("First name"),
'#description' => t("Please enter your first name."),
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#required' => TRUE,
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#required' => TRUE,
);
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function my_module_my_form_validate($form, &$form_state) {
$year_of_birth = $form_state['values']['year_of_birth'];
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
}
// Правило создание обработчика почти такое же как и в случае с валидатором.
// Отличие только в том, что вместо '_validate', к названию функции-строителя
// нужно добавить '_submit'.
//
// Обычно в обработчике стоит что-то делать с данными формы, (которые поступают
// поступают внутри переменной $form_state), например, сохранить их в базу.
// Но сейчас мы ограничимся только лишь выводом сообщения на экран, чтобы
// вы убедились, что обработчик действительно работает.
function my_module_my_form_submit($form, &$form_state) {
drupal_set_message(t('The form has been submitted.'));
}
Пример №8:
Создание новой кнопки — сброса формы и демонстрация присваивания кнопке собственного обработчика. Также, мы переместим проверку всех наших полей в главный валидатор формы.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
// Мы убрали базовую валидацию (#required) у всех полей, чтобы
// проверять их только в валидаторе формы.
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#default_value' => t("First name"),
'#description' => t("Please enter your first name."),
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
);
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
// Добавляем кнопку очистки формы. Свойство #validate приказывает
// форме использовать особые валидаторы при нажатии данной
// кнопки, вместо стандартного.
$form['clear'] = array(
'#type' => 'submit',
'#value' => t('Reset form'),
'#validate' => array('my_module_my_form_clear'),
);
return $form;
}
// Добавляем проверку наших полей здесь вместо стандартной базовой.
// Теперь, если пользователь не заполнит поле, он получит сообщение
// об ошибке, а незаполеннное поле отметится красным.
// Если бы мы оставили прежний способ проверки значений (#required),
// то получали бы ошибки тогда, когда пользователь жал бы кнопку Reset
// на форме с пустыми значениями. Это потому, что базовые валидаторы
// выполняются перед валидаторами полей и формы.
function my_module_my_form_validate($form, &$form_state) {
$year_of_birth = $form_state['values']['year_of_birth'];
$first_name = $form_state['values']['first'];
$middle_name = $form_state['values']['middle'];
$last_name = $form_state['values']['last'];
if (!$first_name) {
form_set_error('first', t('Please enter your first name.'));
}
if (!$middle_name) {
form_set_error('middle', t('Please enter your middle name.'));
}
if (!$last_name) {
form_set_error('last', t('Please enter your last name.'));
}
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
}
function my_module_my_form_submit($form, &$form_state) {
drupal_set_message(t('The form has been submitted.'));
}
// Новый валидатор для кнопки Reset. Выставляя здесь значение $form_state['rebuild']
// в TRUE, мы даем указание пропускать обработчик формы. А пропуская обработчик,
// мы выполняем стандартное действией формы — возвращаемся на нее же с пустыми
// значениями (см пример 2).
function my_module_my_form_clear($form, &$form_state) {
$form_state['rebuild'] = TRUE;
}
Пример №9:
Создаем третью кнопку, которая будет добавлять на форму дополнительный набор полей имен и года рождения (реальное применение примера — в форме семейного положения, нужно добавить данные супруги, если пользователь женат). Валидация будет производится и для дополнительных полей тоже.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
function my_module_my_form($form_state) {
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
// Мы изменяем значение по-умолчанию с расчетом на то,
// что на втором шаге формы здесь должны быть ранее
// заполненные значения, а не приглашение к заполнению.
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#default_value' => $form_state['values']['first'], // изменено
'#description' => t("Please enter your first name."),
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#default_value' => $form_state['values']['middle'], // добавлено
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#default_value' => $form_state['values']['last'], // добавлено
);
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
'#default_value' => $form_state['values']['year_of_birth'], // добавлено
);
// Добавляем новые поля на форму
if (isset($form_state['storage']['new_name'])) { // Это поле заполняется
// при нажатии кнопки "Add new name"
$form['name2'] = array(
'#type' => 'fieldset',
'#title' => t('Name #2'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name2']['first2'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#description' => t('Please enter your first name.'),
'#size' => 20,
'#maxlength' => 20,
'#default_value' => $form_state['values']['first2'],
);
$form['name2']['middle2'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#default_value' => $form_state['values']['middle2'],
);
$form['name2']['last2'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#default_value' => $form_state['values']['last2'],
);
$form['year_of_birth2'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
'#default_value' => $form_state['values']['year_of_birth2'],
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
$form['clear'] = array(
'#type' => 'submit',
'#value' => t('Reset form'),
'#validate' => array('my_module_my_form_clear'),
);
// Мы добавляем кнопку "Add another name" только если она еще
// не была кликнута (т.е. на первом шаге). У кнопкий свой валидатор.
if (empty($form_state['storage']['new_name'])) { // Это поле заполняется
// при нажатии кнопки "Add new name"
$form['new_name'] = array(
'#type' => 'submit',
'#value' => t('Add another name'),
'#validate' => array('my_module_my_form_new_name'),
);
}
return $form;
}
// В этом валидаторе мы выставляем значение $form_state['storage']['new_name'],
// чтобы потом в функции-строителе определить, что форма была нажата.
// На этом этапе вы могли уже и заметить, что переменная $form_state
// передается по ссылке, а это означает, что ее изменения будут видны
// и из функции строителя или из обработчика, который обычно выполняется
// после валидатора.
function my_module_my_form_new_name($form, &$form_state) {
$form_state['storage']['new_name'] = TRUE;
$form_state['rebuild'] = TRUE; // Как вы помните, выставление этой
// переменной повлечет перестройку формы.
// Однако текущий $form_state будет доступен
// в функции-строителе.
}
function my_module_my_form_clear($form, &$form_state) {
unset($form_state['values']); // Принудительно очищаем возможные
unset($form_state['storage']); // значения в памяти.
$form_state['rebuild'] = TRUE;
}
// Добавляем дополнительную логику проверки новых полей.
function my_module_my_form_validate($form, &$form_state) {
$year_of_birth = $form_state['values']['year_of_birth'];
$first_name = $form_state['values']['first'];
$middle_name = $form_state['values']['middle'];
$last_name = $form_state['values']['last'];
if (!$first_name) {
form_set_error('first', t('Please enter your first name.'));
}
if (!$middle_name) {
form_set_error('middle', t('Please enter your middle name.'));
}
if (!$last_name) {
form_set_error('last', t('Please enter your last name.'));
}
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
if ($form_state['storage']['new_name']) {
$year_of_birth = $form_state['values']['year_of_birth2'];
$first_name = $form_state['values']['first2'];
$middle_name = $form_state['values']['middle2'];
$last_name = $form_state['values']['last2'];
if (!$first_name) {
form_set_error('first2', t('Please enter your first name.'));
}
if (!$middle_name) {
form_set_error('middle2', t('Please enter your middle name.'));
}
if (!$last_name) {
form_set_error('last2', t('Please enter your last name.'));
}
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error('year_of_birth2', t('Enter a year between 1900 and 2000.'));
}
}
}
// Если закномментировать вызов unset() ниже, после добавления на
// форму новых элементов и последующей ее отправки, вы заметите,
// что форма не очистится, так как в $form_state['storage'] будет
// присутствовать выставленное ранее значение. Поэтому, нам нужно
// принудительно очистить буфер значений формы.
function my_module_my_form_submit($form, &$form_state) {
unset($form_state['storage']);
drupal_set_message(t('The form has been submitted.'));
}
Пример №10:
Тепер сделаем форму по-настоящему пошаговой — с двумя страницами-шагами. Обратите внимание, что пользователь во время работы с формой остается на том же адресе — 'my_module/form', так как по-умолчанию все это работает на POST запросах.
'My form',
'page callback' => 'my_module_form',
'access arguments' => array('access content'),
'description' => 'My form',
'type' => MENU_CALLBACK,
);
return $items;
}
function my_module_form() {
return drupal_get_form('my_module_my_form');
}
// Добавляем дополнительную логику в нашу функцию строитель,
// чтобы обеспечить разделение на в две страницы. Мы проверяем
// значение в $form_state['storage'], чтобы узнать какую страницу
// нужно отображать в данный момент.
function my_module_my_form($form_state) {
// Показываем страницу-2, если выставлено значение $form_state['storage']['page_two']
if (isset($form_state['storage']['page_two'])) {
return my_module_my_form_page_two();
}
// Страница-1 отображается, если не выставленно значение $form_state['storage']['page_two']
$form['name'] = array(
'#type' => 'fieldset',
'#title' => t('Name'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name']['first'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#default_value' => $form_state['values']['first'],
'#description' => t("Please enter your first name."),
'#size' => 20,
'#maxlength' => 20,
);
$form['name']['middle'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#default_value' => $form_state['values']['middle'],
);
$form['name']['last'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#default_value' => $form_state['values']['last'],
);
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
'#default_value' => $form_state['values']['year_of_birth'],
);
// Добавляем дополнительные поля имени
if (!empty($form_state['storage']['new_name'])) {
$form['name2'] = array(
'#type' => 'fieldset',
'#title' => t('Name #2'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
);
$form['name2']['first2'] = array(
'#type' => 'textfield',
'#title' => t('First name'),
'#description' => t('Please enter your first name.'),
'#size' => 20,
'#maxlength' => 20,
'#default_value' => $form_state['values']['first2'],
);
$form['name2']['middle2'] = array(
'#type' => 'textfield',
'#title' => t('Middle name'),
'#default_value' => $form_state['values']['middle2'],
);
$form['name2']['last2'] = array(
'#type' => 'textfield',
'#title' => t('Last name'),
'#default_value' => $form_state['values']['last2'],
);
$form['year_of_birth2'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
'#default_value' => $form_state['values']['year_of_birth2'],
);
}
$form['clear'] = array(
'#type' => 'submit',
'#value' => t('Reset form'),
'#validate' => array('my_module_my_form_clear'),
);
if (empty($form_state['storage']['new_name'])) {
$form['new_name'] = array(
'#type' => 'submit',
'#value' => t('Add another name'),
'#validate' => array('my_module_my_form_new_name'),
);
}
$form['next'] = array(
'#type' => 'submit',
'#value' => t('Next »'),
);
return $form;
}
// Новая функция делает код более универсальным и управляемым.
// На втором шаге у нас будет выбор любимого цвета.
function my_module_my_form_page_two() {
$form['color'] = array(
'#type' => 'textfield',
'#title' => t('Favorite color'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
function my_module_my_form_new_name($form, &$form_state) {
$form_state['storage']['new_name'] = TRUE;
$form_state['rebuild'] = TRUE; // Пропускаем обработчик.
}
function my_module_my_form_clear($form, &$form_state) {
unset($form_state['values']); // Принудительно очищаем возможные
unset($form_state['storage']); // значения в памяти.
$form_state['rebuild'] = TRUE;
}
// Добавляем валидацию второго шага.
function my_module_my_form_validate($form, &$form_state) {
// Здесь делаем проверку второй страницы.
if (isset($form_state['storage']['page_two'])) {
$color = $form_state['values']['color'];
if (!$color) {
form_set_error('color', t('Please enter a color.'));
}
return;
}
$year_of_birth = $form_state['values']['year_of_birth'];
$first_name = $form_state['values']['first'];
$middle_name = $form_state['values']['middle'];
$last_name = $form_state['values']['last'];
if (!$first_name) {
form_set_error('first', t('Please enter your first name.'));
}
if (!$middle_name) {
form_set_error('middle', t('Please enter your middle name.'));
}
if (!$last_name) {
form_set_error('last', t('Please enter your last name.'));
}
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error('year_of_birth', t('Enter a year between 1900 and 2000.'));
}
if ($form_state['storage']['new_name']) {
$year_of_birth = $form_state['values']['year_of_birth2'];
$first_name = $form_state['values']['first2'];
$middle_name = $form_state['values']['middle2'];
$last_name = $form_state['values']['last2'];
if (!$first_name) {
form_set_error('first2', t('Please enter your first name.'));
}
if (!$middle_name) {
form_set_error('middle2', t('Please enter your middle name.'));
}
if (!$last_name) {
form_set_error('last2', t('Please enter your last name.'));
}
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error('year_of_birth2', t('Enter a year between 1900 and 2000.'));
}
}
}
// Изменяем обработчик так, чтобы он правильно работал в зависимости
// от того, на каком шаге была отправлена форма. Если мы на первом шаге,
// то устанавливаем $form_state['storage']['page_two'], после чего форма
// перегрузится и будет знать, что нужно отображать второй шаг.
// Если же форма была отправлена на втором шаге, то следует показать
// пользователю сообщение об успешном завершении операции и
// переместить его на другую страницу.
function my_module_my_form_submit($form, &$form_state) {
// Обработка первого шага
if ($form_state['clicked_button']['#id'] == 'edit-next') {
$form_state['storage']['page_two'] = TRUE; // Устанавливаем флаг для функции-строителя
// Запихиваем значения первого шага в $form_state['storage'],
// чтобы иметь к ним доступ в финальном обработчике на втором шаге.
$form_state['storage']['page_one_values'] = $form_state['values'];
}
// Второй шаг, финальный обработчик.
else {
// Как я уже говорил, на этом этапе обычно производится сохранение
// данный в базу данных. Но мы ограничимся показом сообщения об
// успешном завершении операции.
drupal_set_message(t('Your form has been submitted'));
// Это значение должно быть очищено, чтобы редирект состоялся.
// Дело в том, что если оно не пустое, $form_state['rebuild'] автоматически
// устанавливается в TRUE (см. первый пункт документации:
// http://drupal.org/node/144132
unset ($form_state['storage']);
// Отправляем пользователя на главную страницу
$form_state['redirect'] = '';
}
}
Примечания
Я старался сильно не отходить от оригинала, исправляя только мелкие неточности и ошибки. Однако, замечу, что с приходом шестого друпала, нет никакой необходимости пихать все проверки в общий валидатор формы. Для каждого элемента можно определять свою функцию-валидатор. Например:
$form['year_of_birth'] = array(
'#type' => 'textfield',
'#title' => t('Year of birth'),
'#description' => t('Format is "YYYY"'),
'#default_value' => $form_state['values']['year_of_birth'],
'#element_validate' => array('is_valid_year'),
);
// ...
function is_valid_year($form_element, &$form_state) {
$year_of_birth = $form_element['#value'];
if (!$year_of_birth || ($year_of_birth 2000)) {
form_set_error($form_element['#name'], t('Enter a year between 1900 and 2000.'));
}
}
Но стоит помнить, что валидаторы элементов выполняются после валидатора формы.
Кроме того, возможно, кто-то спросит, почему не оборачиваются в t() заголовок и описание пункта меню. Отвечаю заранее — в шестом друпале это делается автоматически.
Хотите что-то добавить?