УрокСоздание своего элемента формы в Drupal 7. Расширяем Forms API.

Сегодня я расскажу о том, как создавать свои элементы форм, которые будут доступны для использования через Forms API. Всю разработку я продемонстрирую на примере создания jSlider Form API с авторскими дополнениями и объяснениями кода. Итак, пошагово.

Шаг первый. Описываем элемент формы.

Для описания элемента в модуле надо имплементировать хук hook_element_info(), в котором надо вернуть массив, ключом которого будет будующий тип элемента, и параметры, которые он может принимать.

/**
 * Implements hook_element_info().
 */
function jslider_form_api_element_info() {
  $types['transfer_slider'] =  array(
    '#input'            => TRUE,
    '#process'          => array('transfer_slider_process'),
    '#element_validate' => array('transfer_slider_validate'),
    '#left'             => t('Left'),
    '#right'            => t('Right'),
    '#theme'            => array('transfer_slider'),
    '#left_value'       => 0,
    '#right_value'      => 100,
    '#size'             => 5,
  );
  return $types;
}

Здесь описан элемент формы так, как его надо будет добавлять в форму. Обработчики элемента будут описаны чуть дальше.

Возможные значения по умолчанию:

  • #input: Булевая переменная. Определяет, несёт ли элемент в себе какие-то данные (даже если он скрыт).
  • #process: Массив функций, которые будут обрабатывать элемент формы. Должен принимать переменные $element, $form_state и $complete_form.
  • #after_build: Массив функций, которые будут запущены после того, как элемент будет построен. Принимает переменные $element и $form_state.
  • #validate: Массив функций, которые будут валидировать форму, в которой описан элемент, при его сабмите. Принимаются переменные $form и $form_state.
  • #element_validate: Массив функций, которые будут валидировать элемент при сабмите. Принимаются переменные $element и $form_state.
  • #pre_render: Массив функций, которые должны быть выполнены перед началом рендера элемента. Функции получают переменные $element и $form_state.
  • #post_render: Массив функций, которые должны быть выполнены после окончания рендера элемента. Функции получают переменные $element и $form_state.
  • #submit: Массив функций, который выполняются при сабмите формы. Функции принимают переменные $form и $form_state.
  • #title_display: Строка, которая отображает как заголовок должен быть отображён. Более подробно тут.

Все остальные параметры, которых нет в этом списке - это уже дополнительные, которые вам надо будет самостоятельно обрабатывать в своих #process фунциях.

Шаг второй. Реализация обработчика элемента.

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

function transfer_slider_process($element) {
 
  // Делаем элемент деревом, чтобы он в себе хранил значения дочерних элементов
  $element['#tree'] = TRUE;
 
  // Для слайдера нам потребуется 2 поля ввода - левое и правое значение слайдера.
  // Из формы, которая будет создана разработчикам, создаём новые элементы с 
  // использованием уже известных элементов Forms API
 
  // Создаём левое поле ввода
  $element['left'] = array(
    '#type'             => 'textfield',
    '#field_prefix'     => $element['#left'],
    '#default_value'    => $element['#left_value'],
    '#element_validate' => array('jslider_form_api_validate_integer'),
    '#size'             => $element['#size'],
  );
 
  // Создаём правое поле ввода
  $element['right'] = array(
    '#type'             => 'textfield',
    '#field_suffix'     => $element['#right'],
    '#default_value'    => $element['#right_value'],
    '#element_validate' => array('jslider_form_api_validate_integer'),
    '#size'             => $element['#size'],
  );
 
  // Возвращаем обновлённый элемент
  return $element;
}

Я опустил в коде некоторые параметы, которые есть в коде модуля, т.к. они в данной ситуации будут лишь захломлять код.

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

Шаг третий. Валидация элемента.

Теперь валидация элемента (она вызывается при сабмите формы):

function transfer_slider_validate($element, &$form_state) {
  // Здесь надо проверить, что сумма, введённая при создании элемента формы равна той,
  // которую мы получили при сабмите формы
  $sum = $element['#left_value'] + $element['#right_value'];
  if (($element['#value']['left'] + $element['#value']['right']) != $sum) {
    form_error($element, t("The sum of left and right values doesn't check with original."));
  }
}

Валидация тоже вполне прозрачна - получаем элемент и проверяем, удовлетворяют ли полученные данные нашим ожиданиям.

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

function jslider_form_api_validate_integer($element, &$form_state) {
  if (!ctype_digit($element['#value'])) {
    form_error($element, t('The value should be a valid integer number'));
  }
}

Шаг четвёртый. Темизация элемента.

Темизация не обязательна. Однако рассказать про неё точно стоит.

В описании нашегокастомного элемента (hook_element_info()) был указан такой параметр:

'#theme'  => array('transfer_slider')

Это значит, что элемент темизируем. Далее друпал вызывает все hook_theme() и ищет там transfer_slider. Поэтому, в модуле его надо описать:

function jslider_form_api_theme() {
  $data['transfer_slider'] = array(
    'render element' => 'element',
  );
  return $data;
}

Здесь в регистр темы добавляется transfer_slider, который получает в качестве параметра элемент формы. Ключ 'render element' означает, что после отработки всех функций темизации элемент надо отрендерить.

Теперь описываем функцию темизации:

function theme_transfer_slider($vars) {
  // Add jQuery UI Slider
  drupal_add_library('system', 'ui.slider');
 
  // Add css and js for slider form element
  $path = drupal_get_path('module', 'jslider_form_api');
  drupal_add_js($path . '/jslider_form_api.js');
  drupal_add_css($path . '/jslider_form_api.css');
}

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

Шаг пятый. Тестируем созданную форму.

Создаём страницу на сайте, на которой будет отображена форма с нашим элементом:

function jslider_form_api_menu() {
 
  $items['admin/settings/jslider_form_api_test'] = array(
    'page callback'     => 'drupal_get_form',
    'page arguments'    => array('example_form'),
    'access arguments'  => TRUE,
  );
 
  return $items;
}

Описываем созданный элемент в форме и действие при его сабмите:

function example_form() {
 
  $form['slider'] = array(
    '#type' => 'transfer_slider',
    '#left_value' => 0,
    '#right_value' => 200,
    '#left' => t('Left text'),
    '#right' => t('Right text'),
    '#size' => 4,
  );
 
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
 
  return $form;
}
 
function example_form_submit($form, &$form_state) {
  $left_value   = $form_state['values']['slider']['left'];
  $right_value  = $form_state['values']['slider']['right'];
  drupal_set_message(
    t('Left value is !left_value. Right value is !right_value.', 
    array('!left_value' => $left_value, '!right_value' => $right_value))
  );
}

Смотрим, проверяем, тестируем. Если всё работает - переходим с следующему шагу.

Шаг шестой. Заключительный.

Теперь добавляем к элементу стили, скрипты, изображения (если надо). Убираем тестовые функции. Убеждаемся, что наша форма работет - и радуемся тому, что мы освоили ещё что-то новенькое.

Для наглядности можете скачать модуль и посмотреть его исходники.

Полезная ссылка: Создание кастомных элементов форм.

Комментарии

Аватар пользователя muaddip
muaddip написал:

В шаге 3, валидация элемента происходит данная проверка:

$sum = $element['#left_value'] + $element['#right_value'];
  if (($element['#value']['left'] + $element['#value']['right']) != $sum) {
    form_error($element, t("The sum of left and right values doesn't check with original."));}

Я суть проверки не понял. Мы в переменную $sum записываем значение полей лефт и райт по умолчанию. Зачем после мы сравниваем введенные значения лефт и райт, со значениями по умолчанию?

28.08.2012 15:11
Аватар пользователя РоманFff
РоманFff написал:

Вопрос не по теме, но
Как программно вывести поле тела (основной текст) формы для ноды.
Или создать форму с body полем, что бы редактор к нему можно было прицепить, например такой как сдесь в комментариях.
Есть http://xandeadx.ru/blog/drupal/247
Но там вся форма сразу и со всеми доп элементами
Если уже описано где-то, тыкните меня в ссылку, а то я ключивых слов для поиска не знаю,
А если не написано ждем ваших статей на эту тему с нетерпением.
Спасибо

08.02.2013 19:41
Аватар пользователя Кряк
Кряк написал:

Приветствую, а как можно прикрутить к описанию в компонентах формы какой-либо визуальный интерфейс чтобы загружать картинки, только не нужно ссылок что кто-то там в интернете пишет, нужен рабочий способ не теория!!

24.01.2014 13:38
Аватар пользователя Кряк
Кряк написал:

Что сайт совсем мёртвый или тут дурогоны а не друпал фанаты?

27.01.2014 02:46

Комментировать