hook_form - значения формы не попадают в базу

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

Аватар пользователя zheka2 zheka2 11 мая 2015 в 22:04

Приветствую

Кастомизирую форму ноды, вывожу select

$form ['field_group'] = array(
    '#type' => 'select',
    '#title' => t('Группа'),
    '#options' => $groups[ $discipline_id ],
    '#default_value' => $group->nid,
    '#maxlength' => 127,
    '#prefix' => '<div id="field-group-replace">',
    '#suffix' => '</div>',
 );

После отправки формы вызываю
$output = ctools_modal_form_wrapper('train_node_form', $form_state);

Выбранное пользователем значение попадает в $form_state['values']['field_group'] и имеет строковый тип. И значение не попадает в базу
В случае если не изменять $form['field_group'] то в $form_state['values']['field_group'] содержится массив ['und'][0]['target_id'] и тогда всё нормально сохраняется.

Всю голову сломал, что я делаю не так?

Комментарии

Аватар пользователя kirill_dan kirill_dan 11 мая 2015 в 23:04

Что такое hook_form и при чем тут ctools? Хотите что-то поправить в форме, то нужно делать hook_form_alter(&$form, &$form_state, $form_id) Ловить идешник вашей формы и переопределить значение, какое вам нужно.

Аватар пользователя Orion76 Orion76 11 мая 2015 в 23:06

Потому что в $form_state['values']['field_group'] должен быть массив: ['und'][0]['target_id'].

Выводите select не в $form ['field_group'],а в $form ['field_group']['und'][0]['target_id'].

Аватар пользователя kirill_dan kirill_dan 12 мая 2015 в 0:00

orion76 wrote:
Потому что в $form_state['values']['field_group'] должен быть массив: ['und'][0]['target_id'].

Выводите select не в $form ['field_group'],а в $form ['field_group']['und'][0]['target_id'].

Только туда нужно выводить не тот массив, который в примере, а массив именно терминов. А вместо 'und' нужно писать LANGUAGE_NONE

Аватар пользователя Виктор Степаньков ака RxB Виктор Степаньк... 12 мая 2015 в 0:10

"kirill_dan" wrote:
Только туда нужно выводить не тот массив, который в примере, а массив именно терминов. А вместо 'und' нужно писать LANGUAGE_NONE

лучше уж тогда ознакомиться с правильной работой с полями, в том числе и с языками полей http://drupalace.ru/lesson/korrektnaya-rabota-s-polyami-v-sedmom-drupale...

Аватар пользователя kirill_dan kirill_dan 12 мая 2015 в 1:19

RxB wrote:
"kirill_dan" wrote:
Только туда нужно выводить не тот массив, который в примере, а массив именно терминов. А вместо 'und' нужно писать LANGUAGE_NONE

лучше уж тогда ознакомиться с правильной работой с полями, в том числе и с языками полей http://drupalace.ru/lesson/korrektnaya-rabota-s-polyami-v-sedmom-drupale...

Ну это самый правильный вариант! Вначале теория, потом практика.

Аватар пользователя zheka2 zheka2 12 мая 2015 в 9:29

Спасибо за ответы

Конкретизирую. Написал модуль (напр schedule), через schedule_node_info создал для него тип материала (train).
Мне нужно чтобы поле группа фильтровалось в зависимости от значения другого поля на форме (дисциплина). Поэтому я добавил функцию train_form и в ней прописал

$form ['field_group'] = array(
        '#type' => 'select',
        '#title' => t('Группа'),
        '#options' => $groups[ $discipline_id ],
        '#default_value' => $group->nid,
        '#maxlength' => 127,
        '#prefix' => '<div id="field-group-replace">',
        '#suffix' => '</div>',

Плюс добавил select для выбора самой дисциплины, другие поля на форме я не объявлял и друпал я так понимаю для них сам создал контролы.
Теперь если я сохраняю форму то те поля которые автоматически добавлены друпалом сохраняются в БД, а группа нет.
Я понимаю что можно через metadata wrappers сохранить вручную, но мне кажется это неправильным.
Может я вообще всё делаю неправильно.
Я попробовал через hook_form_alter в $form ['field_group']['und'] прописать массив описывающий select, но это никак не отразилось на форме.

Аватар пользователя kirill_dan kirill_dan 12 мая 2015 в 13:35

zheka2 wrote:
Спасибо за ответы

Конкретизирую. Написал модуль (напр schedule), через schedule_node_info создал для него тип материала (train).
Мне нужно чтобы поле группа фильтровалось в зависимости от значения другого поля на форме (дисциплина). Поэтому я добавил функцию train_form и в ней прописал

$form ['field_group'] = array(
        '#type' => 'select',
        '#title' => t('Группа'),
        '#options' => $groups[ $discipline_id ],
        '#default_value' => $group->nid,
        '#maxlength' => 127,
        '#prefix' => '<div id="field-group-replace">',
        '#suffix' => '</div>',

Плюс добавил select для выбора самой дисциплины, другие поля на форме я не объявлял и друпал я так понимаю для них сам создал контролы.
Теперь если я сохраняю форму то те поля которые автоматически добавлены друпалом сохраняются в БД, а группа нет.
Я понимаю что можно через metadata wrappers сохранить вручную, но мне кажется это неправильным.
Может я вообще всё делаю неправильно.
Я попробовал через hook_form_alter в $form ['field_group']['und'] прописать массив описывающий select, но это никак не отразилось на форме.

Если вы создали новую форму для контент тайпа, то с чего вы взяли, что друпал что-то об этом знает, а тем более, что-то сам добавит в базу?
Чтобы в базу происходила автоматическая запись, то поле нужно объявлять в сущности типа материала. Да и куда ему писать-то, если таблица для группы не было создана (если я вас правильно понял, то вы не создавали поле группа в материале). А для того, чтобы сделать зависимые поля, то нужно использовать либо form_state, либо form_ajax (что предпочтительней). Либо использовать модуль conditions.

Я мог вас не правильно понять. Но вы и не можете четко сформулировать свою проблему. Вы должны были сообщить, что создали такой-то материал, с такими-то полями. Потом хотите сделать то-то, и получить что-то. А вы все крутитесь вокруг своей form group. Но никто не может прочитать ваши мысли!

Аватар пользователя zheka2 zheka2 12 мая 2015 в 14:23

Все необходимые поля я создал в админке. Все они по submit сохранялись. Дальше мне нужно было изменить порядок заполнения поля Группа. Я объявил hook_form и в $form ['field_group'] запихнул своё описание select'а . Теперь это поле не сохраняется.

"kirill_dan" wrote:
Чтобы в базу происходила автоматическая запись, то поле нужно объявлять в сущности типа материала.
Можно подробнее?

Код сейчас имеет такой вид

function fcschedule_menu()
{

    // cut...
   
    $items['fcschedule/%ctools_js/add_train/%node/%/%'] = array(
        'page callback' => 'fcschedule_node_add_modal_callback',
        'page arguments' => array(1, 3, 4, 5),
        'access arguments' => array('access content'),
    );

    return $items;
}

function fcschedule_node_info()
{
    return array(
        'train' => array(
            'name' => t('Занятия'),
            'base' => 'train',
            'description' => t('Занятия'),
        )
    );
}

function fcschedule_node_add_modal_callback($js = FALSE, $group, $date, $time) {
    global $user;
    // If people aren't using javascript, then I just boot em. sorry. its 2011.
    if (!$js) return "Javascript required";

    // Include your ctools crap here
    ctools_include('node.pages', 'node', '');
    ctools_include('modal');
    ctools_include('ajax');

    // Create a blank node object here. You can also set values for your custom fields here as well.
    $node = (object) array(
        'uid' => $user->uid,
        'name' => (isset($user->name) ? $user->name : ''),
        'type' => 'train',
        'language' => LANGUAGE_NONE,
    );

    $form_state = array(
        'title' => t('Add my conten type'),
        'ajax' => TRUE,
    );

    $form_state['build_info']['args'] = array($node);
    $form_state['build_info']['args']['param_group'] = $group;
    $form_state['build_info']['args']['param_date'] = $date;
    $form_state['build_info']['args']['param_time'] = $time;
    // change this to your type node form
    $output = ctools_modal_form_wrapper('train_node_form', $form_state);

    // This means the form has been exectued
    if (!empty($form_state['executed'])) {
        $output = array();
        // Close the modal
        $output[] = ctools_modal_command_dismiss();

        $output[] = ajax_command_invoke('#shed2-refresh', 'click');
    }

    print ajax_render($output);
    exit;
}

function train_form($node, &$form_state) {
    module_load_include('inc', 'fcschedule', 'fcschedule');

    $form = array();

    $discips = FCShedule::getDisccipArray();
    $groups = FCShedule::getGroupsArray();

    if (isset($form_state['build_info']['args']['param_group'])) {
        $group = $form_state['build_info']['args']['param_group'];
    }
    else {
        $node = node_load($node->nid);
        $group = node_load($node->field_group);
    }

    $discipline_id = isset($form_state['values']['discipline']) ? $form_state['values']['discipline'] : $group->field_discipline[LANGUAGE_NONE][0]['target_id'];

    $form ['discipline'] = array(
        '#type' => 'select',
        '#title' => t('Дисциплина'),
        '#options' => $discips,
        '#default_value' => $discipline_id,
        '#maxlength' => 127,
        '#ajax' => array(
            'event'=>'change',
            'callback' => 'fcschedule_train_discip_change_callback',
            'wrapper' => 'field-group-replace',
        ),
    );

    $form ['field_group'] = array(
        '#type' => 'select',
        '#title' => t('Группа'),
        '#options' => $groups[ $discipline_id ],
        '#default_value' => $group->nid,
        '#maxlength' => 127,
        '#prefix' => '<div id="field-group-replace">',
        '#suffix' => '</div>',
    );

    return $form;
}

function fcschedule_train_discip_change_callback($form, $form_state) {
    return $form['field_group'];
}

Аватар пользователя kirill_dan kirill_dan 12 мая 2015 в 19:32

zheka2 wrote:
Все необходимые поля я создал в админке. Все они по submit сохранялись. Дальше мне нужно было изменить порядок заполнения поля Группа. Я объявил hook_form и в $form ['field_group'] запихнул своё описание select'а . Теперь это поле не сохраняется.
"kirill_dan" wrote:
Чтобы в базу происходила автоматическая запись, то поле нужно объявлять в сущности типа материала.
Можно подробнее?

Код сейчас имеет такой вид

function fcschedule_menu()
{

    // cut...
   
    $items['fcschedule/%ctools_js/add_train/%node/%/%'] = array(
        'page callback' => 'fcschedule_node_add_modal_callback',
        'page arguments' => array(1, 3, 4, 5),
        'access arguments' => array('access content'),
    );

    return $items;
}

function fcschedule_node_info()
{
    return array(
        'train' => array(
            'name' => t('Занятия'),
            'base' => 'train',
            'description' => t('Занятия'),
        )
    );
}

function fcschedule_node_add_modal_callback($js = FALSE, $group, $date, $time) {
    global $user;
    // If people aren't using javascript, then I just boot em. sorry. its 2011.
    if (!$js) return "Javascript required";

    // Include your ctools crap here
    ctools_include('node.pages', 'node', '');
    ctools_include('modal');
    ctools_include('ajax');

    // Create a blank node object here. You can also set values for your custom fields here as well.
    $node = (object) array(
        'uid' => $user->uid,
        'name' => (isset($user->name) ? $user->name : ''),
        'type' => 'train',
        'language' => LANGUAGE_NONE,
    );

    $form_state = array(
        'title' => t('Add my conten type'),
        'ajax' => TRUE,
    );

    $form_state['build_info']['args'] = array($node);
    $form_state['build_info']['args']['param_group'] = $group;
    $form_state['build_info']['args']['param_date'] = $date;
    $form_state['build_info']['args']['param_time'] = $time;
    // change this to your type node form
    $output = ctools_modal_form_wrapper('train_node_form', $form_state);

    // This means the form has been exectued
    if (!empty($form_state['executed'])) {
        $output = array();
        // Close the modal
        $output[] = ctools_modal_command_dismiss();

        $output[] = ajax_command_invoke('#shed2-refresh', 'click');
    }

    print ajax_render($output);
    exit;
}

function train_form($node, &$form_state) {
    module_load_include('inc', 'fcschedule', 'fcschedule');

    $form = array();

    $discips = FCShedule::getDisccipArray();
    $groups = FCShedule::getGroupsArray();

    if (isset($form_state['build_info']['args']['param_group'])) {
        $group = $form_state['build_info']['args']['param_group'];
    }
    else {
        $node = node_load($node->nid);
        $group = node_load($node->field_group);
    }

    $discipline_id = isset($form_state['values']['discipline']) ? $form_state['values']['discipline'] : $group->field_discipline[LANGUAGE_NONE][0]['target_id'];

    $form ['discipline'] = array(
        '#type' => 'select',
        '#title' => t('Дисциплина'),
        '#options' => $discips,
        '#default_value' => $discipline_id,
        '#maxlength' => 127,
        '#ajax' => array(
            'event'=>'change',
            'callback' => 'fcschedule_train_discip_change_callback',
            'wrapper' => 'field-group-replace',
        ),
    );

    $form ['field_group'] = array(
        '#type' => 'select',
        '#title' => t('Группа'),
        '#options' => $groups[ $discipline_id ],
        '#default_value' => $group->nid,
        '#maxlength' => 127,
        '#prefix' => '<div id="field-group-replace">',
        '#suffix' => '</div>',
    );

    return $form;
}

function fcschedule_train_discip_change_callback($form, $form_state) {
    return $form['field_group'];
}

Момент номер один. Я не вижу логику работы аякса. Он просто возвращает сам себя, грубо говоря. В $group->nid по умолчанию у вас всегда константа, которую вы тянете из аргументов $form_state, которые туда попали при построении динамического окна. Вам нужно создать валидацию элемента, в которой вы сможете построить свою логику для аякса. Например, запихнуть в $form_state новые данные, которые вам нужны.
Момент номер два. Что значит изменить порядок заполнения? Точнее скажите. Если у вас уже есть созданный материал с двумя полями группы и дисциплины, то вам незачем городить такой огород с инициализацией материала и инициализацией для него полей.

Зачем function train_form($node, &$form_state), если вы поля создали вручную? Тем более, что в диалоговое окно вы передаете форму друпала.

Аватар пользователя zheka2 zheka2 12 мая 2015 в 21:28

Спасибо

"kirill_dan" wrote:
Что значит изменить порядок заполнения? Точнее скажите. Если у вас уже есть созданный материал с двумя полями группы и дисциплины, то вам незачем городить такой огород с инициализацией материала и инициализацией для него полей.
Идея следующая, есть ноды занятия, группы, дисциплины. В ноде занятия есть поле группа, а каждая группа ссылается на дисциплины:
Группа 1 - Танцы
Группа 2 - Танцы
Группа 1 - Бокс
Группа 2 - Бокс

Если в форму занятия вывести группы селектом, то получится список Группа 1, Группа 2, Группа 1, Группа 2. Пытался через "Entity Reference View Widget" но во-первых долго набивка материала производится, во-вторых, закрывая модальное окно выбора группы (View Widget) сразу закрывается и модальное окно редактирования занятия. Как решить я не понял и остановился на варианте с 2мя селектами, в первый я вывожу все дисциплины, а группы уже по ней фильтрую

"kirill_dan" wrote:
Я не вижу логику работы аякса. Он просто возвращает сам себя, грубо говоря. В $group->nid по умолчанию у вас всегда константа, которую вы тянете из аргументов $form_state, которые туда попали при построении динамического окна. Вам
Пример я этот взял от сюда https://gist.github.com/mrconnerton/1979037 . Перезаполнение списков работает нормально, сначала вызывается train_form, если указали дисциплину, то в #options списка групп попадают групп только по ней. Следом за ней вызывается fcschedule_train_discip_change_callback который возвращает только что сформированный массив field_group
С $group->nid похоже действительно ошибка, но это влияет только на #default_value списка групп.

"kirill_dan" wrote:
Зачем function train_form($node, &$form_state), если вы поля создали вручную? Тем более, что в диалоговое окно вы передаете форму друпала.
Вот тут я честно говоря совсем не понял. Поля я создал в базе, в train_form для этого типа материала я описываю форму.

Аватар пользователя kirill_dan kirill_dan 12 мая 2015 в 23:15

"zheka2" wrote:
Вот тут я честно говоря совсем не понял. Поля я создал в базе, в train_form для этого типа материала я описываю форму.

Если вы создали поля в материале, который уже существует, то вам никакую форму описывать вообще не нужно. Она уже создана друпал. Тот хук, что вы пытаетесь использовать, это наследие шестой версии, которое уже не используется в семерке. В колбеке вызова диалогового окна у вас происходит вызов уже существующей формы: $output = ctools_modal_form_wrapper('train_node_form', $form_state); Вы какую форму вызываете? Правильно: train_node_form, а не train_form. Но объявив train_form вы вмешались в работу формы и переопределили ее. Кстати, название не правильное. Да и если вы сами создали материал, то нет смысла хук инфо создавать. Хотели из модуля создать материал с полями? Нужно было это через сущности делать. Да то такое.

Тут ошибка вообще в построении архитектуры! Группы - это таксономия! Дисциплины - это тоже таксономия! А занятия уже ноды.
Например:
Спорт
-Бокс
-Плавание
-Футбол
Гуманитарные науки
-Поэзия
-Философия

У вас тут получается иерархический словарь таксономии! Где группы - это первая вложенность, а дисциплины - вторая.

Материал занятия уже содержит подробное описание и другие поля. Создается нода занятия. Выбирается Term reference из словаря таксономии Спорт (если список иерархического выбора), появляется новое поле - выбираете Плавание. ВСЕ!!!!
Теперь вы можете в последствии производить поиск, сортировку, фильтрацию, как вам будет угодно.

Аватар пользователя zheka2 zheka2 13 мая 2015 в 12:49

Спасибо за развёрнутый ответ

"kirill_dan" wrote:
Тот хук, что вы пытаетесь использовать, это наследие шестой версии, которое уже не используется в семерке.
Не знал, в документации об этом ни слова, а с 6кой я не связывался
"kirill_dan" wrote:
Да и если вы сами создали материал, то нет смысла хук инфо создавать. Хотели из модуля создать материал с полями? Нужно было это через сущности делать. Да то такое.

Материал создал модуль, а поля я уже сам накидал. Вообще hook_node_info я бы и не объявлял, просто, как я понял, он нужен для hook_form.
"kirill_dan" wrote:
Тут ошибка вообще в построении архитектуры! Группы - это таксономия! Дисциплины - это тоже таксономия! А занятия уже ноды.
Спасибо, сначала хотел сделать через таксономию, сейчас уже не вспомню почему переиграл. Про иерархически словарь даже не думал
Ещё раз спасибо.

Аватар пользователя kirill_dan kirill_dan 14 мая 2015 в 18:13

"zheka2" wrote:
Спасибо, сначала хотел сделать через таксономию, сейчас уже не вспомню почему переиграл. Про иерархически словарь даже не думал
Ещё раз спасибо.

Не за что. Удачи вам.