[РЕШЕНО] И это всё о ней. Форма API. Заморочка с _form_alter.

Главные вкладки

Аватар пользователя starro-serge starro-serge 15 августа 2010 в 13:50

Заранее прошу прощения за многословие, но короче не объяснить.

Пишу свой модуль.
В модуле присутствуют:
function win_constructor_menu() {
$items['win_constructor'] = array(
...
return $items;
}
function win_constructor_page() {
$output = drupal_get_form('win_constructor_nameform');
return $output;
}
function win_constructor_nameform() {
...
return $form;
}
function win_constructor_form_alter(&$form, &$form_state, $form_id) {
...
}
function win_constructor_nameform_validate($form, $form_state) {
....
}
function theme_win_constructor_nameform($form) {
...
drupal_render($form);
return $output;
}

Чтобы проследить как это всё работает, расставил drupal_set_message по модулям.
При загрузке страницы получаем следующую последовательность:
* PAGE win_constructor_page
* CONSTRUCTOR win_constructor_nameform
* ALTER win_constructor_form_alter
* in ALTER $form_state[values][win_opens_radios] =
* in ALTER $form[win_opens][win_opens_radios] = 0
* THEME theme_win_constructor_nameform
* ALTER win_constructor_form_alter
* in ALTER $form_state[values][win_opens_radios] =
* in ALTER $form[win_opens][win_opens_radios] =
* ALTER win_constructor_form_alter
* in ALTER $form_state[values][win_opens_radios] =
* in ALTER $form[win_opens][win_opens_radios] =

Тут уже непонятно, зачем после темизации ещё два раза влезаем в ALTER.
И главное, при этом форма $form уже недоступна.
Ну да и ладно бы. Но вот тут и начинается кино.

После нажатия кнопки в форме и её возврата имеем следующее:
* PAGE win_constructor_page
* CONSTRUCTOR win_constructor_nameform
* ALTER win_constructor_form_alter
* in ALTER $form_state[values][win_opens_radios] =
* in ALTER $form[win_opens][win_opens_radios] = 0
* VALIDATE win_constructor_nameform_validate
* in VALIDATE $form_state[values][win_opens_radios] = 1
* in VALIDATE $form[win_opens][win_opens_radios] = 0
* PAGE win_constructor_page
* CONSTRUCTOR win_constructor_nameform
* ALTER win_constructor_form_alter
* in ALTER $form_state[values][win_opens_radios] =
* in ALTER $form[win_opens][win_opens_radios] = 0
* THEME theme_win_constructor_nameform
* ALTER win_constructor_form_alter
* in ALTER $form_state[values][win_opens_radios] =
* in ALTER $form[win_opens][win_opens_radios] =
* ALTER win_constructor_form_alter
* in ALTER $form_state[values][win_opens_radios] =
* in ALTER $form[win_opens][win_opens_radios] =

В VALIDATE доступны и $form, и $form_state.

После CONSTRUCTOR и до THEME в ALTER, где я бы и хотел немножко отрихтовать форму в зависимости от полученных от формы значений в $form_state, этот самый $form_state оказывается недоступным.
Вроде бы так быть не должно.
И где собака зарыта?

P.S. Не знаю, имеет ли это к делу отношение, но на всякий случай
наличие или отсутствие в VALIDATE
$form_state['rebuild'] = TRUE;
картины не меняет

Комментарии

Аватар пользователя starro-serge starro-serge 15 августа 2010 в 14:24

P.P.S. Забыл сказать, что все полученные из формы значения сбрасываются в CONSTRUCTOR после VALIDATE, что очевидно, но невероятно.
Поскольку, судя по http://drupal.org/node/165104, после валидации мы должны выходить на темизацию непосредственно, минуя конструктор.
Правда, судя по той же схеме, после валидации там нет места и для _form_alter. Однако после #theme стоит #post_render.
Однако, мы же заходим _form_alter.
В общем, голова кругом идёт.

Аватар пользователя brg brg 10 ноября 2015 в 11:46

Нужно наверное смотреть весь код модуля и наверное список того что ещё есть на сайте.
Потому что на мой взгляд в теории всё верно и в предоставленных кусках кода ошибки вроде нет.
Попробовал проверить на одном и своих сайтов, там все верно выводится:
* PAGE
* FORM
* ALTER
* THEME

Пример и его код.
После сабмита тоже все правильно выводится.
Нужно наверное больше информации или попробуйте воспользоваться функцией debug_print_backtrace, она покажется кто и откуда вызывает функции.

Аватар пользователя starro-serge starro-serge 15 августа 2010 в 15:52

Нужно убрать немного информационного шума.
Повторные ALTER после THEME идут от двух других форм, расположенных на той же странице (login, search).
Ими можно пренебречь.
Остался один вопрос, ключевой.
Почему при возврате формы ПОСЛЕ VALIDATE процесс генерации формы запускается сначала через PAGE -> CONSTRUCTOR -> ALTER -> THEME.
Вроде как в CONSTRUCTOR (или уже в PAGE без разницы) теряется при этом $form_state, что логично.
И это вместо продекларированного в документации
VALIDATE -> ALTER -> THEME.

Аватар пользователя starro-serge starro-serge 15 августа 2010 в 15:57

Ну прописана у меня в модуле своя темизация формы и двух видов полей в ней.
Разве это может как-то повлиять на процесс её обработки после возврата?

Аватар пользователя brg brg 15 августа 2010 в 16:23

Вообще в теории в validate ты вроде как форму изменить не сможешь, так как туда не передается переменная form, которая потом используется для render, ибо валидате только для проверки уже сформированной формы.

Последовательность выполняется правильно, в этом можешь убедиться открыв файл form.inc функцию drupal_process_form(...)

Я так понял смысл заключается в том, что тебе нужно внести изменения в форму через alter в случае если не проходит валидация (ну т.е. в каком то поле что-то не то введено)?

Попробуй для этого в alter проверять массив $form['#parameters'], там ты сможешь увидеть что ввел пользователь и подредактировать форму как тебе нужно если юзер ввел какие то неподходящие данные.

-----------------------
Опять таки на примере http://stroynn.ru/test
Там валидация никогда не проходит, но если ввести 111, то форма поменяется и в функции валидации можно будет проверить новое поле.
Я правильно понял задачу?

Аватар пользователя starro-serge starro-serge 15 августа 2010 в 16:28

Так я и не пытаюсь форму в валиде трогать.
Точно в альтере хочу над ней поизгаляться.
Но совет не катит.
После конструктора $form['#parameters']- пустой массив
Array ( [0] => win_constructor_nameform [1] => Array ( [storage] => [submitted] => [post] => Array ( ) ) )

Аватар пользователя starro-serge starro-serge 15 августа 2010 в 18:47

brg wrote:
Странно ... у меня в 'post' находятся переменные собственно полученные POST запросом.
http://drupal.ru/files/test.module_0.txt - тут все работает, если у вас пусто, значит нужно смотреть что ещё там у вас есть.

Где-то я туплю.

# VALIDATE win_constructor_nameform_validate
# SUBMIT win_constructor_nameform_submit
# PAGE win_constructor_page
# CONSTRUCTOR win_constructor_nameform
# ALTER win_constructor_form_alter win_constructor_nameform
# THEME theme_win_constructor_nameform

После VALIDATE я пролетаю в SUBMIT.
И оттуда процесс начинается сначала, раз нет редиректа.
Значит, в VALIDATE я не определяю форму как непрошедшую валидацию.
Внимание вопрос!
Тупой.
Как объявить в VALIDATE, что форма не прошла валидацию.
Кроме как через генерацию ERROR.
То есть ничего не выдавать пользователю.
Просто попасть в ALTER.

Аватар пользователя starro-serge starro-serge 15 августа 2010 в 16:56

Потому что у Вас всё работает штатно по схеме
VALIDATE -> ALTER -> THEME.
А у меня с вывертом
VALIDATE -> PAGE -> CONSTRUCTOR -> ALTER -> THEME.
Как я и предполагал, уже в PAGE $form_state теряется.
И это уже по ту сторону добра и зла.
Откуда ноги растут - тайна сия велика есмь.

Аватар пользователя brg brg 15 августа 2010 в 19:08

Наверное будет правильнее сделать через JS, т.е. через друпаловский ahah или просто при помощи jQuery .. на мой взгляд это будет более так сказать по феншую Smile

А то, что ты спрашиваешь .. нужно думать .. потому что вообще не очень логически правильная задача :/ Лучше тогда делать при помощи мультистеп форм, это лучше вот тут почитать:
http://drupal.org/node/212903
http://drupal.org/node/144132#multistep
http://www.andrewyager.com/content/view/51/27/
http://matslats.net/drupal-6-multistep-form-example

Аватар пользователя starro-serge starro-serge 16 августа 2010 в 9:19

brg wrote:
Наверное будет правильнее сделать через JS, т.е. через друпаловский ahah или просто при помощи jQuery .. на мой взгляд это будет более так сказать по феншую Smile

А то, что ты спрашиваешь .. нужно думать .. потому что вообще не очень логически правильная задача

Насчёт не очень логически позволю себе не согласиться.
Очень даже логично.
Форма - это, прошу прощения за тавтологию, именно форма организации диалога.
Именно диалога, а не игры в угадайку.
И бинарное ограничение результатов валидации - "верно-неверно" неоправданно.
Должен быть вариант "продолжить" с возможностью перекомпановки формы.
И кто мешает Друпалу ввести нечто вроде form_not_validate(), чтоб в валидации объявить этот третий путь.
Чтоб после этого Друпал не ломился в сабмит а плавно переходил в альтер.

Так что само введение понятия "Валидация" с двумя выходами - это существенное ограничение, на мой взгляд.

В конце концов моё немудрёное желание в чистом РНР исполняется лёгким движением руки.
Почему тут труднее должно быть?

Ну да ладно, можно было бы обойтись и тем, что имеется.
В конце концов я б вывернулся, если б всё, что декларируется в Друпал и пишется о нём было истинной.

Вот у John K. VanDyk, вроде авторитетный таварисч, именно для такого случая в разделе
"Form Rebuilding" пишется, что я в конструкторе могу эту ситуацию отловить.
Прошу прощения за цитату:

In your form definition function, you would have something like this:
function formexample_nameform($form_id, $form_state = NULL) {
// Normal form definition happens.
...
if (isset($form_state['formexample']['spam_score']) {
// If this is set, we are rebuilding the form;
// add the captcha form element to the form.
...
}
...
}

То есть в конструктор должен передаваться старый $form_state
Чего бы проще тогда.
Так ведь нет этого.

А за ссылки спасибо.
Будем учиться дальше.

Аватар пользователя brg brg 16 августа 2010 в 12:38

К сожалению данный специалист ошибается, поэтому книгам сторонних авторов нужно верить с некоторой долей недоверия Smile
http://www.drupalbook.com/node/141 В данном месте книги была допущена ошибка.
Попробуйте использовать:
function win_constructor_nameform($form_state) {
После отправки формы в нем будет содержаться отправленные значения. Если более конкретно, то $form_state['post']

Про диалог в форме я ни сколько не спорю, просто я бы наверное немного по-другому сделал бы.

Аватар пользователя starro-serge starro-serge 16 августа 2010 в 17:52

Красиво я изложил, только глупость сморозил.
form_set_error(); - без параметров -вроде бы и есть form_not_validate() как я обозвал.
Но вот в чём загвоздка.
И в примере http://stroynn.ru/test, и в моём модуле ALTER ПРЕДШЕСТВУЕТ VALIDATE, а не наоборот как в документации.
Или я уж совсем притупел.

А насчёт совета: "Попробуйте использовать:
function win_constructor_nameform($form_state) {"

то я в раскорячке, грубо говоря.
И в http://www.drupalbook.com/node/141
и у у John K. VanDyk пишется одно и то же
function formexample_nameform($form_id, $form_state = NULL) {
И именно этого я желаю всей душой.
Получить на входе в конструктор $form_state со всеми делами.
Но у меня он передаётся уже пустой

Аватар пользователя brg brg 18 августа 2010 в 10:15

$form_state передается первым элементом, а не вторым .. правильно использовать function formexample_nameform($form_state)
У меня всё отлично работает. Проверьте ещё раз, если проблема останется, выложите свой модуль, я попробую помочь.

Аватар пользователя starro-serge starro-serge 18 августа 2010 в 11:14

brg wrote:
$form_state передается первым элементом, а не вторым .. правильно использовать function formexample_nameform($form_state)
У меня всё отлично работает. Проверьте ещё раз, если проблема останется, выложите свой модуль, я попробую помочь.

Так и есть, первым.
Но как им верить после этого?
Ведь пишут совсем не так.

Ещё раз спасибо за заботу, дорогой. Всё путём.
Будем учиться дальше.