

Друпал содержит множество всевозможных форм. Иногда, не все они выглядят так, как бы вам хотелось. Изменение друпаловских форм это тема, которую многие разработчики встречают дружными стонами неудовольствия и непонимания. Между тем, достаточно уяснить всего два метода их изменения и все становится намного яснее и проще.
Итак, существует два способа модификации форм в Друпале. Один из них реализуется через написание модуля. Второй — посредством темизации. Оба способа, в конечном счете, допускают изменения HTML представления формы, поэтому вы вольны выбирать какой из них использовать, исходя из конкретной задачи или личных пристрастий.
Однако, существуют два случая, когда придется реализовать изменения через модуль:
$_POST. А удаление поля в модуле, удаляет поле везде и насовсем. В большинстве случаев это не является серьезной проблемой, но все же, вы должны знать о такой возможности, прежде чем отдать предпочтение методу темизации.В данном примере не будет никаких изменений функциональности, поэтому будет показано применение обоих методов изменения формы. На самом деле, код в обоих случаях очень похож, и опирается на две важные вещи: 1) ID формы 2) некоторые знания Forms API Друпала.
Наш пример покажет, как изменить в форме, расположенной на странице Мои учетные данные > Изменить (http://example.com/user/123/edit), скажем, заголовка "Имя пользователя" на "Логин ID".
Все формы в Друпале имеют уникальные идентификаторы (ID). Вы можете без особого труда найти ID формы в HTML коде формы. На вашем сайте, зайдите на страницу с формой и загляните в ее исходный код (Firebug или Dragonfly идеально подходят для этой задачи) и посмотрите на скрытые поля, ближе к верху формы.

Там должно быть одно, имеющее name="form_id":
<input id="edit-user-edit" type="hidden" value="user_edit" name="form_id"/>В дальнейшем, вам понадобится значение (value) этого поля. В нашем случае, это "user_edit". В шестом Друпале, ID этой формы будет равен "user_profile_form".
Примечание: Возможно вы заметили, что сама форма имеет HTML аттрибут id="user-edit". Важно помнить, что это не тот ID, что нам нужен. Наш идентификатор почти всегда имеет нижнее подчеркивание (_) вместо черточек (-).
Теперь приступим к созданию функций. Конечно же, названия и размещение функций для тем и модулей кардинально отличаются. Также, не забудьте подставить во все эти примеры названия настоящих тем или модулей. Итак, поехали.
Для этого, вставьте вашу функцию в файл template.php вашей темы. Вам нужно будет создавать новую функцию для каждой формы, которую вы захотите изменить.
<?php
/**
* Темизация формы редактирования пользователя.
*
* Функцию следует называть по типу названиетемы_formid.
*/
function названиетемы_user_edit($form) {
// Тут совершаем наши мега-изменения
}
?>В шестом Друпале все становится чуть интереснее. Многие формы в шестерке теперь имеют свои шаблоны или "зарегистрированные" функции. Для них, вы можете просто отредактировать шаблон или перекрыть соответствующую функцию.
Для форм, которые не имеют шаблонов или зарегистрированных функций, нам нужно не только перекрыть функцию темизации, но и зарегистрировать ее в системе темизации, чтобы Друпал о ней знал. Больше информации о регистре темизации можно найти на страницах документации. Наша форма как раз такой и является. Как только добавите следующие функции в ваш template.php, обновите регистр темизации, сбросив кеш на странице "Администрация" > "Производительность".
Вот функция регистрации, которая нам нужна:
<?php
/**
* Реализация hook_theme.
*
* Регистрирует пользовательские функции темизации
*/
function названиетемы_theme() {
return array(
// ID формы
'user_profile_form' => array(
// Формы всегда имеют аргумент form
'arguments' => array('form' => NULL),
),
);
}
?>А вот и сама функция темизации. Она очень походит на функцию из пятерки, за исключением ID формы:
<?php
/**
* Темизация формы редактирования пользователя.
*
* Функцию следует называть по типу названиетемы_formid.
*/
function названиетемы_user_profile_form($form) {
// Тут совершаем наши мега-изменения
}
?>
Используя этот способ, нам потребуется реализовать хук hook_form_alter() и поместить его в .module-файле нашего модуля. Еще одной особенностью данного способа является то, что вы можете изменить сколько угодно форм внутри лишь одной функции, используя условие на ID формы.
<?php
/**
* Реализация hook_form_alter().
*
* Функцию следует называть по типу названиемодуля_form_alter.
*/
function названиемодуля_form_alter($form_id, &$form) {
// лучше использовать конструкцию switch, потому что редко когда
// дело ограничивается одной формой, а в switch довольно просто
// добавлять новые условия
switch ($form_id) {
// Наш ID формы
case 'user_edit':
// Наши мега-изменения
break;
}
?><?php
/**
* Реализация hook_form_alter().
*
* Функцию следует называть по типу названиемодуля_form_alter.
*/
function названиемодуля_form_alter(&$form, $form_state, $form_id) {
// лучше использовать конструкцию switch, потому что редко когда
// дело ограничивается одной формой, а в switch довольно просто
// добавлять новые условия
switch ($form_id) {
// Наш ID формы
case 'user_profile_form':
// Наши мега-изменения
break;
}
?>Итак, вы достали нужный ID формы, создали необходимые функции, осталось только узнать, какие элементы содержит формы и с чем вам придется работать. Самый простой способ узнать содержание массива формы — это вывести его на экран функцией print_r() и глянуть в исходный код страницы. Например, так:
<?php
function themename_user_edit($form) {
return print_r($form);
}
?>Нам нужно отыскать в массиве формы то, что следует поменять, а потом превратить это в PHP код наших функций, используя небольшую магию Forms Api. Итак, что представляет собой массив $form для нашей формы:
Array
(
[account] => Array
(
[#type] => fieldset
[#title] => Информация об учетной записи
[name] => Array
(
[#type] => textfield
[#title] => Имя пользователя
[#weight] => 0
[#maxlength] => 60
[#description] => Например, "VasyaPupkin". Должен состоять из латинских символов (a-z), цифр, дефиса начинаться и заканчиваться буквой или цифрой и содержать не более 20 символов.
)
)
[pass] => Array
(
[#type] => password_confirm
// ...
)
// ...
[comment_settings] => Array
(
[#type] => fieldset
[#title] => Настройка комментариев
[#collapsible] => 1
[#collapsed] =>
// ...
)
)
Итак, нам нужно просто пройтись по этому массиву и найти то, что нужно поменять. Как вы помните, мы хотим поменять заголовок поля "Имя пользователя". Исходя из массива формы, нам нужно взяться за [account][name][#title]:
$form['account']['name']['#title'] = t('Login ID');Более того, если у нас разыграется аппетит, мы можем, к примеру, сделать "Настройку комментариев" закрытой по-умолчанию:
$form['comment_settings']['#collapsed'] = TRUE;Или же вообще удалить ее с формы:
unset($form['comment_settings']);Вы можете делать с формой абсолютно все, главное не забывать подсматривать в шпаргалку по Forms API ;)
Итак, теперь вы имеете необходимые знания для того, чтобы справиться с любой друпаловской формой.
<?php
/**
* Темизация формы редактирования пользователя.
*
* Функцию следует называть по типу названиетемы_formid.
*/
function названиетемы_user_edit($form) {
$output = '';
// Выводим на экран массив формы, чтобы узнать с чем работать
// print_r($form)
// Используя структуру Forms API, совершаем изменения
$form['account']['name']['#title'] = t('Login ID');
// Вызов drupal_render() для всего массива $form, чтобы убедится, что
// все элементы формы выведены на экран
$output .= drupal_render($form);
return $output;
}
?>Функция регистрации:
<?php
/**
* Реализация hook_theme.
*
* Регистрирует пользовательские функции темизации
*/
function themename_theme() {
return array(
// ID формы
'user_profile_form' => array(
// Формы всегда имеют аргумент form
'arguments' => array('form' => NULL),
),
);
}
?>Функция темизации:
<?php
/**
* Темизация формы редактирования пользователя.
*
* Функцию следует называть по типу названиетемы_formid.
*/
function названиетемы_user_profile_form($form) {
$output = '';
// Выводим на экран массив формы, чтобы узнать с чем работать
// print_r($form)
// Используя структуру Forms API, совершаем изменения
$form['account']['name']['#title'] = t('Login ID');
// Вызов drupal_render() для всего массива $form, чтобы убедится, что
// все элементы формы выведены на экран
$output .= drupal_render($form);
return $output;
}
?><?php
/**
* Реализация hook_form_alter().
*
* Позволяет изменять любую форму сайта. Вы можете не только менять,
* удалять или добавлять элементы к форме, но и добавлять дополнительные
* валидаторы или обработчики для формы. Функцию следует называть
* по типу названиемодуля_form_alter.
*/
function modulename_form_alter($form_id, &$form) {
// лучше использовать конструкцию switch, потому что редко когда
// дело ограничивается одной формой, а в switch довольно просто
// добавлять новые условия
switch ($form_id) {
// Наш ID формы
case 'user_edit':
// Выводим на экран массив формы, чтобы узнать с чем работать
// print_r($form)
// Используя структуру Forms API, совершаем изменения
$form['account']['name']['#title'] = t('Login ID');
break;
}
}
?><?php
/**
* Реализация hook_form_alter().
*
* Позволяет изменять любую форму сайта. Вы можете не только менять,
* удалять или добавлять элементы к форме, но и добавлять дополнительные
* валидаторы или обработчики для формы. Функцию следует называть
* по типу названиемодуля_form_alter.
*/
function modulename_form_alter(&$form, $form_state, $form_id) {
// лучше использовать конструкцию switch, потому что редко когда
// дело ограничивается одной формой, а в switch довольно просто
// добавлять новые условия
switch ($form_id) {
// Наш ID формы
case 'user_profile_form':
// Выводим на экран массив формы, чтобы узнать с чем работать
// print_r($form)
// Используя структуру Forms API, совершаем изменения
$form['account']['name']['#title'] = t('Login ID');
break;
}
}
?>Для укрепления знаний, можете взглянуть еще раз на статью Человеческая форма комментирования. Там использовалась темизация.
hook_alter? К сожалению, мне известен только один альтер для форм, он же hook_form_alter(). Поиск тоже не дал результатов. Мы, часом, не говорим об одном и том же?
# | andypost
Я говорю про http://drupal.org/node/144132#form-id-alter и http://drupal.org/node/114774#drupal-alter
# | vebdiver
Спасибо за статью. Будет полезна многим.
Попутно есть вопрос. А как насчет темизации форм которые размещены в блоках а не на в контенте?
Крутил вертел с формой авторизации - ничего. Все что нужно сделать - добавить яваскрипт для подстановки текста "Логин" и "Пароль" при пустом значении поля и удаление этого текста при получении фокуса полем. Может кто уже делал?
В примерах к "hook_form_alter() в Друпал5" закрывающие фигурные скобки пропущены, к 6й версии тоже
гм. позновательно. А эта система, Друпал, она вообще для чего то конкретно предназначена? Ну типа как ВП для блогов.
На друпале можно делать очень много вещей — начиная с блогов и галерей, и заканчивая социальными сетями и интернет магазинами.
# | DesTincT
user_login() таким образом не перехватывается + print_r($form) выводит саму форму и не ее параметры О_о
# | appolo
а можно ли как-нибудь поместить форму (список+кнопка) на страницу профиля пользователя, чтобы,например, отсылать определенное сообщение пользователю?
поначалу думал что в case можно прописать user_view....ан нет, нет такой формы..
# | анонимус
У меня не срабатывает ни один из способов. Копирую ваш код из hook_form_alter() для Друпал6 в template.tpl.php и ничего не меняется. Что может быть не так?
По поводу темизации выпадающего спика. Никак не могу найти ответ или просто не могу увидить его... :(
Делаю форму с элементом:
$form['sgsdata']['sgs_text'] = array(
'#type' => 'select',
'#title' => t('Select text type'),
'#attributes' => array('class' => 'sgs-new'),
'#default_value' => $form_state['values']['sgs_text'],
'#options' => array(
t('main'),
t('prior'),
t('new')
)
);
Но нужно в выпадающем списке пункт "new" выделить красным жирным шрифтом.
Хотел прицепить дополнительно к option class="big-red":
...
<option value="3">new</option> // так получается
...
...
<option value="3" class="big-red">new</option> // как-то так нужно
...
но никак не пойму как это сделать.
Так вот - как дополнить в option еще class или id ?
Зарание Спасибо !!!
сам себе отвечаю и делюсь с Вами :(
в form.inc где строится список элементов для select
function form_select_options($element, $choices = NULL)
...
$options .= '<option value="'. check_plain($key) .'"'. $selected .'>'
. check_plain($choice) .'</option>';
...просто нет такой возможночти.
Павел, если уж вы до этого дошли своими силами, то отвечу вам как можно это все же реализовать. Объявите свой тип поля через hook_elements(), например advanced_select, скопируйте стандартную темизацию select'а, но замените form_select_options() на вашу модифицированную версию. Это наиболее правильный способ решения задачи.
# | Макс
Здравствуйте!
Почти закончил создание сайта на Друпале и рад, что неплохо и быстро с ним разобрался, но застрял на одном месте и уже несколько дней не могу с него сдвинуться.
Необходимо сделать темизацию формы user-edit так, чтобы все поля выводились на одной странице.
Перерыл весть друпал.орг и друпал.ру, но ничего не нашел и никто не смог мне помочь. Может Вы сможете? Буду очень благодарен.
Вы плохо искали. На drupal.org определенно должно быть решение. В пятерке я использовал такой код:
function mymodule_user($type, &$edit, &$user, $category = NULL) {
if ($type == 'form' && $category == 'account') {
return profile_form_profile($edit, $user, 'Личное');
}
}
Где Личное — это название вкладки Profile.
Возможно для шестерки все сработает без изменений, но это уже ваша забота.
# | Макс
Я даже и не думал смотреть в сторону этого хука. Спасибо!
Но вот теперь функция profile_form_profile($edit, $user, 'account'); ничего не возвращает не для какой категории.
Проверяю таким методом: drupal_set_message(print_r(profile_form_profile($edit, $user, 'account'),TRUE));
Друпал стоит 5-ый.
'account' — это не категория профиля. Категорию профиля вы сами создаете.
# | Макс
Ну это понятно. Я просто для примера вставил. У меня есть категория "Деятельность". Её подставлял тоже, но ничего не возвращается.
Создал модуль с именем "lenta". В нем переопределил хук
function lenta_user($type, &$edit, &$user, $category = NULL) {
if ($type == 'form' && $category == 'account') {
drupal_set_message(print_r(profile_form_profile($edit, $user, 'Деятельность'),TRUE));
return profile_form_profile($edit, $user, 'Деятельность');
}
}
То, что возвращает функция никак на отображаемый профиль не влияет. Пробовал и return 0.
Найдите функцию profile_user(), вставьте там дебажный код и посмотрите что она принимает и возвращает на вашей вкладке 'Деятельность'. По результатам этой проверки, составьте правильный код. Он точно должен работать, я же его не придумал из головы.
PS. Вы уже не первый коммент пишете, используйте кнопочки подсветки кода, она нужна не только для красоты, а и улучшает восприятие.
# | Макс
Разобрался! Большое Вам спасибо!
Я по ошибке сохранял файл с модулем не в той кодировке :)
а вот как насчот темизации рендеринга формы? т.е. теги, в которые этот массивчик оборачивается? Можно сделать какуюто функцию типа drupal_render, но если глянуть сырцы, в самой функцие этих тегов нет. там используется функция theme. Я подставлял форме #theme ключ, свою тему, и реализовывал эту функцию ниже (имятемы_моя функция).
В основном принтовал то шо приходит, но ничего не принтовалось, ничего не менялось. Надеюсь на Ваш ответ
Есть два подхода:
1. Теги засовывать в #prefix/#suffix нужного элемента или всей формы (это проще и легче всего).
2. Как вы правильно сказали, использовать #theme для всего элемента либо формы. При этом, вам следует проделать все действия, описанные в первой части статьи для регистрации функции темизации.
Т.е. мне нада сначала зарегистрировать фукнцию темы, чтобы потом ей обработать форму. Т.е. я походу, переливаю из пустого в порожнее. Я же впервый раз регистрирую функцию для темизации формы, а потом во второй раз, для темизации drupal_render. Хотя можно и в первой функции правильно отобразить форму, как я понял. Ну вобщем, спасибо, буду експерементировать
Если вам нужно темить всю форму, то, конечно, необязательно делать это по второму разу. drupal_render() всего лишь рекурсивно вызывает себя же для дочерних элементов формы (которые вызываеют стандартную темизацию элементов), то есть вы можете написать например такое:
function mytheme_some_form($form) {
// Например, нам нужно вывести два поля на форме каким-то особым образом перед всеми остальными
$output .= '<table><tr><td class="class1">'. drupal_render($form['element1']) . '</td>';
$output .= '<td class="class2">'. drupal_render($form['element2']) . '</td></tr></table>';
// После вызова drupal_render() для дочернего, он уже не выведется, при
// опращении drupal_render() для родительского, т.е. следующая строка
// выведет только оставшиеся неотрисованными элементы формы (но ее
// нужно вызвать, так как там есть скрытые служеьные поля).
$output .= drupal_render($form);
return $output;
}но в то же время, вы можете где-то на form_alter или для той же функции темизации всей формы сделать такое:
$form['element1']['#theme'] = 'element1_custom_output';
но после этого вам нужно будет зарегать функцию темизации element1_custom_output(), и определить ее в том же духе, что и главной функции темизации формы.
И конечно, чем городить это все, проще сделать:
$form['element1']['#prefix'] = '<table><tr><td class="class1">';
$form['element1']['#suffix'] = '</td>';
$form['element2']['#prefix'] = '<td class="class2">';
$form['element2']['#suffix'] = '</td></tr></table>';
$form['element1']['#weight'] = -10;
$form['element2']['#weight'] = $form['element1']['#weight'] + 0.01;
# | guide
Это понятно, но я вот в модуле upload видел, что можно вывести форму таблицей. Не могли бы вы описать, такой способ как theme('table'); с опцией drag&drop
# | анонимус
Такие умные "Drupal" и автор статьи!... А слабо один раз просто стделать такой http://www.phpform.org/ "генератор форм" и навсегда закрыть тему?
# | Sinkora
У меня вопрос: при темизации форм каждым из 2-х способов, производительность движка как-нибудь изменяется в худшую сторону?
И второй вопрос:
Например, есть какой-нибудь готовый модуль. И вот мы хотим изменить вид и функционал одной из форм, генерируемых этим модулем. Что в таком случае более предпочтительно: создание нового модуля, который будет "надстройкой" данного модуля, или лучше редактировать сам модуль? Но при редактировании самого модуля, мы в дальнейшем столкнемся с трудностями и неудобством его обновления...
Пишу для формы редактирования материала в своём модуле:
form_alter (&$form, $form_state, $form_id) {
global $user;
if ($user->uid==1) {
drupal_set_message ($form['author']['#weight']);
$form['author']['#weight'] = -100;
$form['author']['#title'] = 'test';
drupal_set_message ($form['author']['#weight']);
}
return $form;
}
Вес файлдсета меняется (20 -> -100), заголовок тоже, а так и не всплывает :(
Ссылки с других сайтов
Все молчат как партизаны.