API форм

Закончил перевод очередной статьи из цикла "20 API за 20 дней". В ней речь идет, пожалуй, о самом обсуждаемом API Друпала - об API форм. В заметке коротко рассматриваются вопросы создания, валидации и сохранения формы, ее темизации и использования AHAH.

Оригинальная статья: The Forms API.
Автор: Кайл Каннингэм.

API форм

Об API форм Друпала сказано уже немало, однако для разработчиков, ранее работавших на других платформах, концепция может оказаться непростой. Любая статья о фреймворках была бы неполной без обсуждения способов их применения.

API форм Друпала обеспечивает разработчиков возможностью динамически создавать, изменять и валидировать формы на веб-сайтах. Вместо того, чтобы создавать разметку для каждого элемента формы, разработчики задают форму через массив, в котором содержатся инструкции для Друпала о том, как сгенерировать форму и показать ее пользователям. API форм также рассказывает Друпалу, как валидировать данные формы и что с этими полученными данными делать.

Настоящая мощь API форм заключается в том, что сторонние модули могут изменять любые формы в процессе их генерации. Разработчики могут создавать новые поля, добавлять дополнительные условия валидации и динамически обрабатывать сохранение данных формы. Такого рода расширяемость - уникальная черта Друпала - представляет собой весьма необычную модель разработки веб-приложений.

Зачем вообще API форм?

Формы - неотъемлемая часть Интернета, позволяющая веб-сайтам собирать пользовательские данные. Иметь в наличии платформу, позволяющую легко обрабатывать сохранение форм, - очень важно, особенно если учесть, какие решения были раньше.

На рассвете Интернета сохраняемые формы обычно обрабатывали через cgi-скрипты. Эти скрипты как правило использовали однопоточные дистрибы Perl и были ужасно медленными в сравнении с современными технологиями. С некоторыми из основных проблем разработчики, администраторы и операторы сайтов боролись с помощью обработки форм запутанными bash-скриптами, встречаясь при этом с проблемами потоков на сервере при валидации сохраненных данных форм для предотвращения их некорректного использования. Часто встречающимся способом положить веб-сайт было сохранение формы с большим количеством информации, чем предусматривалось обработчиком данных. Это приводило к проблемам нехватки памяти, блокировке потоков, ошибкам сегментации и многим другим гадостям.

И каждый фреймворк для разработки веб-приложений был вынужден разбираться с таким положением дел. Подход, выработанный в сообществе Друпала, в чем-то уникален, он подчеркнуто гибок на многих уровнях. Подход включает:

  • автоматическую генерацию форм через основанные на массивах прототипы;
  • возможность динамически модифицировать структуру формы в сторонних модулях;
  • возможность валидировать данные формы при сохранении;
  • возможность добавлять функции-обработчики сохраняемых данных;
  • возможность модифицировать порядок сохранения формы;
  • возможность динамически перенаправлять пользователя после сохранения формы на заданное содержимое.

Если задуматься, пройти все эти уровни обработки формы - серьезная задача для PHP-приложения. Друпал также обеспечивает возможности для кэширования форм, чтобы предотвратить необходимость их генерации с нуля при каждом запросе страницы. Это улучшает производительность, но может привести к некоторым серьезным последствиям для разработчика.

Простой пример

Создание формы в Друпале - относительно простой процесс. Каждая форма объявляется внутри PHP-функции как показано ниже:

function myform($form_state) {
  $form['mytext'] = array(
    '#type' => 'textfield',
    '#title' => t('My Text'),
    '#description' => t('Enter your text'),
    '#required' => TRUE,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}

Итак, как мы уже обсудили, каждая форма в Друпале - структурированный массив значений. Этот массив позволяет вам задавать тип используемого элемента формы (текстовое поле, селектбокс, радио-кнопки и т.д.), указывать, является ли данный элемент формы обязательным к заполнению, и другие полезные атрибуты. Для описания элементов формы существует множество атрибутов, их полный список можно найти на странице описания Forms API на сайте api.drupal.org.

Теперь чтобы сгенерировать и отобразить форму на веб-странице, все, что требуется сделать - это вывести ее через простую команду:

print drupal_get_form('myform');

Этот код возвращает простую форму с текстовым полем и кнопкой сохранения.

Валидация и обработка сохранения

Когда форма сгенерирована, Друпалу необходимо знать, что делать с информацией, приходящей через нее. Обработка сохранения формы состоит из двух шагов: валидации и сохранения.

Валидация - это шаг в процессе сохранения формы, на котором полученные данные проверяются на предмет корректности. Друпал автоматически проверяет, заполнены ли обязательные поля. В функции валидации разработчики могут разместить код, в котором будут проверять значения формы на соответствие требованиям их приложения. Например, если вы ожидаете в определенном поле число, на этом шаге вы проверяете, что введено именно число. В Друпале есть несколько специальных функций, позволяющих установить сообщение об ошибке и попросить пользователя ввести правильные значения.

Сохранение - это шаг в процессе сохранения формы, на котором мы манипулируем данными. Если форма не может пройти валидацию, данные никогда не достигнут стадии сохранения. Разработчики используют функции сохранения для записи данных, полученных через онлайн-форму, и для остальной необходимой обработки.

Как и все прочее в API форм, функции валидации и сохранения определяются динамически. Самый простой способ создать эти функции - дать им такое же имя, как у формы. Для формы из примера выше, функции myform_validate и myform_submit будут автоматически обрабатывать сохранения этой простой формы.

function myform_validate($form, &$form_state) {
  // Проверяем, что все на месте
  if (strlen($form_state['values']['mytext']) 
    form_set_error('mytext', t('The text you entered must be at least 80 characters long.'));
  }
}
function myform_submit($form, &$form_state) {
  drupal_set_message($form_state['values']['mytext']);
}

Функция form_set_error выставляет ошибку для заданного элемента формы. В данном случае мы проверяем, что наше текстовое поле mytext содержит текст из хотя бы 80 символов, а если это не так - выставляем ошибку формы. Ошибка отображается пользователю через статусное сообщение, кроме того, элемент формы будет подсвечен красным, чтобы пользователь знал, где именно произошла ошибка.

Функции myform_validate и myform_submit - это дефолтные функции обратного вызова для валидации и сохранения формы соответственно. Использующиеся по умолчанию функции, можно заменить, или же добавить дополнительные функции валидации и сохранения. В Друпале для этого есть два массива, входящих в описание формы:

// Добавляем функцию обратного вызова для валидации
$form['#validate'][] = 'my_new_validation_function';
// добавляем еще одну функцию обратного вызова - для сохранения
$form['#submit'][] = 'my_new_submission_function';

Эти функции будут вызываться в порядке размещения в массивах. Их использование даст вам возможность сделать поведение своих модулей более утонченным.

Темизация форм

Хотелось бы создавать не только функциональные, но и красивые формы! К счастью, Друпал обеспечивает методику, следуя которой вы сделаете формы сексапильными. Первое, что нам потребуется сделать - это рассказать слою темизации Друпала, что мы хотим кое-что темизировать. Это делается через hook_theme.

function mymodule_theme() {
  return array(
    'myform' => array(
      'arguments' => array('element' => NULL),
    ),
  );
}

Таким образом мы информируем слой темизации о том, что будем применять темизирующую функцию. Напомним, что Друпал автоматически генерирует название для этой функции. В нашем случае, поскольку мы сказали слою темизации, что темизируем форму myform, функция будет называться просто theme_myform. В темизирующей функции мы можем использовать drupal_render() для генерации HTML-кода элементов нашей формы.

function theme_myform(&$element) {
  // Темизирующая форму функция всегда принимает первым аргументом
  // элементы формы. Теперь мы можем добавлять свою разметку
  // или еще как-то изменять отображаемую форму.
  $output = '<h2>' . t('Our customisation to the theme') . '</h2>';
  $output .= drupal_render($element['mytext']);
  $output .= drupal_render($element['submit']);
  //Эта строчка отрендерит все элементы формы, которые еще не были отрендерены.
  //Полезно для скрытых полей, хорошо использовать, чтобы убедиться, что мы ничего не упустили
  $output .= drupal_render($element);
  return $output;
}

Единственное, что нам осталось, это модифицировать массив формы, чтобы указать в нем нашу новую темизирующую функцию.

$form['#theme'] = 'myform';

Эта техника может быть использована на любом уровне массива элементов формы. То есть, хотите ли вы изменить внешний вид всей формы или только текстового поля - Друпал делает эту задачу легко решаемой.

AJAX в формах

Теперь, когда мы охватили основы, можно добавить пикантности. Допустим, мы хотим сохранять форму через AJAX. Это лишь вопрос добавления небольшого кода к нашей форме. Прежде всего, у кнопки сохранения формы есть свойство #ahah (AHAH расшифровывается как Asynchronous HTML and HTTP - Асинхронный HTML и HTTP). Оно позволяет Друпалу узнать, что при нажатии кнопки должно произойти действие AJAX.
Вы наверное заметили, что один из элементов массива AHAH - это 'path'. Он содержит внутренний путь Друпала, к которому произойдет обращение при нажатии кнопки. Так что создадим его через hook_menu() Друпала.

function mymodule_menu() {
  $items['ajaxsubmit'] = array(
    'page callback' => 'ajaxsubmit',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
}
function myform_sweet($form_state) {
  $form['mytext'] = array(
    '#prefix' => '
',
'#suffix' => '

',
'#type' => 'textfield',
'#title' => t('My Text'),
'#description' => t('Enter your text'),
'#required' => TRUE,
);

$form['submit'] = array(
'#type' => 'textfield',
'#value' => 'submit',
'#ahah' => array(
'path' => 'ajaxsubmit',
'wrapper' => 'wrapper'
'method' => 'replace',
'effect' => 'fade',
),
);

return $form;
} function ajaxsubmit() {
// Здесь устанавливаем значение
drupal_set_message(t("Value set!"));
$messages = theme('status_messages');
return drupal_json('data' => $messages, 'status' => TRUE);
}

Вот, собственно, и все. Есть еще изюминки, которые мы не рассмотрели из соображений рациональности. Более подробно с AHAH в Друпале можете ознакомиться здесь.

Погодите, и еще

API форм - один из самых обсуждаемых разделов Друпала. Перечисление всего, что вы можете делать с его помощью, явно выходит за рамки статьи. Вот некоторые темы, которые вам, возможно, захочется изучить:

Убедитесь, что прочли все наши статьи про API — в них мы исследуем основные инструменты расширения функций Друпала.

-----------------------------------
Итого на данный момент переведены все существующие заметки цикла. Авторы из компании Trellon уже две недели не выкладывают новых статей - то ли заняты, то ли сдулись. Но если статьи все же появятся - будут обязательно переведены.

Комментарии