Hi,
I have added a field in my form using hook_form_alter() but when I try to save the node, this field is not in $node.
I understand that custom strings can be added to $node in hook_submit, but how do I read the value of this field which is added by hook_form_alter() ?

Comments

pnick107’s picture

Flow:The default form has two fields, Title and TMP. When second TMP1's value is changed, the field is replaced with a new field (tmp2).
Enter a value in tmp2, and hit save. $node is printed but it does not hold the value of tmp2.

Further if I create a new field in hook_form_alter itself then $node can read the value, but if it is created in the callback function (like in the code below) the value of $tmp2 is not saved in array $node.

Here is the full module code
wc_ajxtest.info

; $Id$
name = Ajax Test
description = A sample module to test Ajax.
package = Test
core = 7.x
version = 7.x-0.1

files[] = wc_ajaxtest.module

wc_ajaxtest.module


/*
 * Implementation of hook_node_info(): Define node
 */
function wc_ajaxtest_node_info(){                                                   
    return array(
    'wc_ajaxtest' => array(
      'name' => t('wc Ajax Test'),
      'base' => 'wc_ajaxtest',                                                  // Base for callback = hook name
      'description' => t("A smaple module to test Ajax."),
      'has_title' => TRUE,                                                      
      'title_label' => t('Title'),
      'has_body' => TRUE,
      'body_label' => t('Body'),
    )
  );
}

/*
 * Hook_form() displays the fields of the content
 */
function wc_ajaxtest_form($node, &$form_state){
    $form = array();
        // Add fields
        $form['title'] = array(
        '#type' => 'textfield',
        '#title' => t('Title'),
            
    );
        $form['tmp']= array(
        '#type' => 'textfield',
        '#title' => t('TMP1'),
        '#prefix' => '<div id="wc_tmp">',
        '#suffix' => '</div>',
        '#default_value' => 'ab',
        '#required' => FALSE,
    );
    
    return $form;
}

function wc_ajaxtest_form_alter(&$form, &$form_state, $form_id) {
    if($form_id ='wc_ajaxtest_node_form'){
        $form['title']['#default_value'] = "Test";           // Basic implementation 
        $form['tmp']['#ajax'] = array(
            'callback' => 'wc_ajaxtest_t2',
            'wrapper' => 'wc_tmp',
            'method' => 'replace',
        //  'progress' => array('type' => 'none')                                // Don't show loader
        );
    }
    return $form;
}

 function wc_ajaxtest_t2(&$form, &$form_state){
    $form['tmp2']= array(
        '#type' => 'textfield',
        '#title' => t('TMP2'),
        '#default_value' => 'yy',
        '#required' => FALSE,
    );
    return ($form['tmp2']);  
}

function wc_ajaxtest_insert(&$form, &$form_state){
      echo "<pre>"; print_r($form); echo "<pre>";
      die("killer text");
}
nevets’s picture

Nothing personal, but your code is wacked.

Since you are building the form it is not clear why you would even use hook_form_alter().

And ajax callbacks like wc_ajaxtest_t2() can not add form elements (the form function needs to handle that), instead the ajax callback is responsible for returning the change form element(s).

pnick107’s picture

Come on Dude,
This is just a sample module.

wc_ajaxtest_t2() is a callback from hook_form_alter() and DOES add form elements.
Thanks for your input though.

jaypan’s picture

If you want to add a 'field' (note: not part of the Drupal Field API) to a node in hook_form_alter(), you have to do the following:

1) Implement hook_schema() to create a database table to hold your values:

function my_module_schema()
{
  $schema['my_module_field'] = array
  (
    'description' => 'Holds custom form values for my node',
    'fields' => array
    (
      'nid' => array
      (
        'description' => 'The unique Node ID from the {node} table',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'value' => array
      (
        'description' => 'The value of the field',
        'type' => 'text',
      ),
    ),
    'primary key' => 'nid',
  );

  return $schema;
}

Note: If you are creating this table after your module is already installed, you will need to implement hook_update_n() with db_create_table() in order to create your database table.

2) Add your field to the node form in hook_form_alter():

function my_module_form_alter(&$form, &$form_state, $form_id)
{
  if($form_id == 'some_node_form')
  {
    $form['my_field'] = array
    (
      '#type' => 'textfield',
      '#title' => t('Enter some text'),
    );
  }
}

3) Save your value in hook_node_insert()

function my_module_node_insert($node)
{
  db_query(
    'INSERT INTO {my_module_field} (nid, value) VALUES (:nid, :value)',
    array(
      ':nid' => $node->nid,
      ':value' => $node->my_field,
    )
  );
}

4) Implement hook_node_update() for the update functionality:

function my_module_node_update($node)
{
  db_query(
    'UPDATE {my_module_field} SET value = :value WHERE nid = :nid',
    array(
      ':value' => $node->my_field,
      ':nid' => $node->nid,
    )
  );
}

5) Load the value in hook_node_load($node)

function my_module_node_load()
{
  if($node->type == 'some_node_type')
  {
    $node->my_field = db_query(
      'SELECT value FROM {my_module_field} WHERE nid = :nid',
      array(
        ':nid' => $node->nid,
      )
    );
  }
}

6) Optionally implement hook_node_view() to display the field when the node is viewed:

function my_module_node_view($node)
{
  if($node->type == 'some_node_type')
  {
    $node->content['my_field'] = array
    (
      '#markup' => check_plain($node->my_field),
      '#prefix' => '<p>',
      '#suffix' => '</p>',
    );
  }
}

With the above process, a database table is created in hook_schema(), the field is added to the node form in hook_node_alter(), the value is saved in hook_node_insert(), the value is updated in hook_node_update(), the value is loaded in hook_node_load() and the value is displayed in hook_node_view().

Note: You would only ever use this process when adding fields to node types that are not created by your module. If you were doing this in the module that created the node type, you would use different hooks.

Contact me to contract me for D7 -> D10/11 migrations.

pnick107’s picture

Thanks for the detailed explanation and code Jaypan.
I indeed have wc_modulebase_schema() which creates all it's tables.

The issue is with the following line

 ':value' => 'my_field', // in hook_insert()

Using 'my_field' like this places the string my_field in the DB, and does not provide the user input.

I initially used $node->tmp2 for the value, but it came empty (when I printed array it even the key didn't show up).

My question is How do I read this value in any of the functions.

jaypan’s picture

Sorry that was a typo on my part, it should have been $node->my_field, not my_field. Unfortunately I can't edit the post anymore to fix it.

Contact me to contract me for D7 -> D10/11 migrations.

nevets’s picture

I fixed your post above for you.

jaypan’s picture

Thanks Nevets.

Contact me to contract me for D7 -> D10/11 migrations.

jaypan’s picture

As to your problem, show us your hook implementation, so we can try to debug it.

Edit: looking at your original code, it appears you may be trying to access the value in hook_insert() rather than hook_node_insert(). It's likely not available in hook_insert().

Contact me to contract me for D7 -> D10/11 migrations.

pnick107’s picture

Hello Jay,
I implemented hook_node_insert() as shown below, but tmp2 is still not in the array.

function wc_ajaxtest_node_insert($node){
    echo "<pre>"; print_r($node); echo "<pre>";
    die("killer text");
}

Thanks!

nevets’s picture

This is where my comment on adding fields comes in. The complete form must be built by the form function, the ajax callbacks only responsibility is to return the change element(s). Any fields added by will not be available in validation or submit functions.

jaypan’s picture

Nevets is correct. All alterations to the form must happen in the form definition, or the form_alter function, (or a function called from one of these).

Contact me to contract me for D7 -> D10/11 migrations.

pnick107’s picture

Interesting.
Thanks Guys for clearing this up. I have a followup question though.

What I am trying to achieve here is I want to ask my user how many tabs he wants. When he fills the number, using hook_form_alter() I create that many tabs. I want all of them to have same number of fields (For example you can consider my question to be "How many members do you have in your family?" and depending on this answer that many tabs are made to save the first name and last name of each family member. Actual case is more complex and requires separate tabs).
Now if I create one tab and it's buttons in hook_form() can I create others in hook_form_alter() programmatically using a similar approach as "add more" ?

Thanks for your Time!

jaypan’s picture

Yes, you can do this in hook_form_alter().

Contact me to contract me for D7 -> D10/11 migrations.

pnick107’s picture

Thanks Jay,
I will give this a try in a few days and let you know how it went.