I have a custom content type and trying to alter its node form. The form has two fields, field1 and field 2. Either field1 or field 2 is visible to the user (based on user role). The goal is to let a user enter a value in one (visible) field and to calculate the value of the other (hidden) field, so that both values could be saved to the db. For instance, as in converting kilograms to pounds - the user enters the value in one field, then the second value is saved to the db after the calculation is performed. I am ok with doing form_hook_alter and have been using hook_node_presave to calculate the values. It works, but the problem is that I have to make the fields required or the users simply do not enter the data. Since I have to make both fields required, the hidden field has to be populated with the result of the calculation BEFORE the form validation stage and hook_node_presave functions after the validation is done, so the form never passes validation with required fields and never gets to the calculation. I tried a few different ways, but with no success. Is it possible to populate field values with other values, in this case a calculated value based on the value of another field, after submit and before validation? Do I need to mod $form_state['values'] array? There must be a simple way to do it!

Comments

zbricoleur’s picture

The simplest way to do it would be with a jQuery snippet. Then you wouldn't have to alter the form array at all.

BlueCat’s picture

That's possible, but, still, how about computations with hook_form_alter() to avoid doing it on the client? Where should any manipulation with entered field values go so that any calculation and (re-)population of fields can be done BEFORE validation kicks in?

BlueCat’s picture

I was digging deeper and found form_set_value() that can be used in the validate function. Has anyone used it successfully for populating a field with a different value?

zbricoleur’s picture

It isn't the easiest function to get a handle on. Have a look at this post: https://drupal.org/node/566622

bianca_oliveira’s picture

Hi!

I am having the same problem. I added a button in node forms with:

function mymodule_form_node_form_alter (&$form, &$form_state, $form_id) {

  $form['#validate'][] = 'mymodule_validate';
  
  $form['actions']['new_option'] = array(
    '#type' => 'button',
    '#value' => t('New Option'),
    '#weight' => 15,
    '#submit' => array('mymodule_newOption_submit'),
  );
}

I saw the page recommended and put the code below:

function mymodule_validate($form, &$form_state) {
  
  $field['#parents'] = array("body", 0, 'value');
  form_set_value($field, "BLABLABLA", $form_state);
}

When I click on "New Option" button nothing happens. What am I doing wrong?

zbricoleur’s picture

What wasn't made clear in that page I linked was that the array you assign to $field['#parents'] needs to start at $form_state['values']. I.e., if you're trying to change the body field, you would use simply $field['#parents'] = array('body');. To see why that's so, install the Devel module, and then put dpm($form_state); in your validate function so you can see, when you submit the form, the structure of the $form_state array.

bianca_oliveira’s picture

Hi!

Thank you for you reply. I tried:

function mymodule_validate($form, &$form_state) {
  $field['#parents'] = array('body');
  form_set_value($field, "BLABLABLA", $form_state);
}

And:

function mymodule_validate($form, &$form_state) {
  $field['#parents'] = array('values', 'body');
  form_set_value($field, "BLABLABLA", $form_state);
}

But nothing. I am using Drupal 7 and I saw that the API changed from:

form_set_value($form_item, $value, &$form_state)

to

form_set_value($element, $value, &$form_state)

But the way to write is the same. But I tried too:

function mymodule_validate($form, &$form_state) {

  $parents = $form['body']['#array_parents'];
  $element = drupal_array_get_nested_value($form, $parents);

  form_set_value($element, "BLABLABLA", $form_state);
}

I am using devel module and printing $form_state I tried all combinations of array that I could and I've tried copy the usage from others modules but nothing happens. I click on button, print $form_state and nothing change.

Any idea? =/

zbricoleur’s picture

I was assuming D6. For D7, it looks like you have to use this instead:
$field['#parents'] = array('body', 'und', 0, 'value');

bianca_oliveira’s picture

Hi!

Thanks a lot for your reply. Now it is working with the Submit Button.

My idea is use it with a Button because I want show the form with modified version of the field before really submit. Inside a #validate or #submit function in a "button" it is not working.

I would like:

1) Before Submit a content, on validate function, modify a field and rebuild the form again (abort submit) - I've tried $form_state['rebuild'] = TRUE but it doesn't change the field content.

or

2) Have a new button on the form (using form_alter) that will call a validate function and change the field content. (and automatically will return to form)

Thanks.

BlueCat’s picture

Not sure why all of the complexity with arrays for replacing a single value. I am looking here http://api.drupal.org/api/drupal/includes--form.inc/function/form_set_va... and at the functions that call form_set_value() and it seems very straight forward. At the same time, I can't get my code to calculate and insert a calculated value. It should be very simple: if entered value exists if one field, then use it to compute a value for the other field that was left blank and populate the field with that value. Here is my code. I see the value - as entered - appearing in the db, but I do not see the calculated value there. So it is either not getting calculated or not getting inserted into the db. I am also seeing something weird with ajax callback - my form_alter changes (Submit button label changes from "Add Reading" back to standard node "Save") do not persist in the form after user submits the from. Did I mess up the ajax callback somehow?


/**
 * Implements hook_form().
 */

// altering node form to mod submit button label and add ajax callback 
// for reloading just the form - not the entire page on submit

function my_module_form_alter(&$form, &$form_state, $form_id) {
  if( $form_id == 'reading_node_form') { 
     $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Add Reading'),
    '#weight' => 100,
    '#validate' => array('my_module_validate','node_form_validate'), // add custom validate handler
    '#submit' => array('node_form_submit'),                                     // this is the default submit handler
    '#ajax' => array(       
        'wrapper' => 'reading-node-form',  //css id of the element - wrapper that needs to be affected on callback
        'callback' => 'my_module_callback',
        'clear' => TRUE,
        'effect' => 'fade'
        ),
    );
}
  return $form;
}


//
// AJAX CALLBACK - is there a better way to rebuild the form?
//

function my_module_callback($form, &$form_state) {
  $node = new stdClass();
  $node->type = 'reading';
  return drupal_get_form('node_form', $node);
}


//
// VALIDATE
//
// one field is hidden and the other field is showing
// both fields are required
// the user fills out the visible field (which is visible is determined elsewhere)
// the other empty field needs to be calculated before standard validation for "required" kicks in
// trying to use form_set_value(), but the values do not appear in the db

function my_module_validate($form, &$form_state) {
  // Assign submitted values from two fields to variables
  $us_entered = $form_state['values']['field_us_value'];
  $metric_entered = $form_state['values']['field_metric_value'];

  // Check if user input is present in either field
  if($us_entered == '') {
    //us entered value is blank - so we need to alter its blank submitted value in $form_state to the computed value
    $us_calculated = $metric_entered * 18; 
    form_set_value($form['field_us_value'], $us_calculated, $form_state);
  }
  if($metric_entered == '') {
    //metric entered value is blank - so we need to alter its blank submitted value in $form_state to the computed value
    $metric_calculated = $us_entered * 0.055; 
    form_set_value($form['field_metric_value'], $metric_calculated, $form_state);
  }
return $form;
}

bianca_oliveira’s picture

Which Drupal version are you using?

BlueCat’s picture

Drupal 7

zbricoleur’s picture

Instead of form_set_value($form['field_us_value'], $us_calculated, $form_state); try:

$x['#parents'] = array('field_us_value', 'und', 0, 'value');
form_set_value($x, $us_calculated, $form_state);

And I've noticed that I have to go to the database and empty the cache tables sometimes to make my changes take effect.

BlueCat’s picture

Let me try, though I do not understand why this is necessary. Based on the form_set_value() examples I am seeing, this is not something I commonly see used in other modules. I will report on the results....

BlueCat’s picture

changed the statements, but the same problem persists. The originally entered value saves fine and the one that needs to be calculated does not go into the db. I am not sure at what stage the process dies. Everything looks good. Does something else need to be included?

BlueCat’s picture

I also added a custom submit handler to see what $form_state value it would print to screen, but I am getting blanks, which is strange, because at least one of these values was entered by a user and is being successfully deposited into the db.


function my_module_submit($form, $form_state) {
  $us = $form_state['values']['field_us_value'];
  $metric = $form_state['values']['field_metric_value'];

drupal_set_message(t('We see the US value as - %us - and Metric value as - %metric !', 
array('%us' => $us, '%metric' => $metric)));
}

mesch’s picture

If you don't make the cck fields required, you can manually check in your custom validation that the field is populated.

BlueCat’s picture

Right now the fields for this custom content type are not required. But it would be good to figure out why this does not work and I think it would be very valuable to the community as well. form_set_value would work just fine here. I must be missing something....

BlueCat’s picture

Any other suggestions on why form_set_value does not seem to update field value in this code?

BlueCat’s picture

Always run something like debug($form). It just brought be back to thinking that we have a node form here, from a custom content type, not a made-from-scratch form. So when I was writing in my validate function:

form_set_value($form['field_us_value'], $us_calculated, $form_state);

I was not actually accessing the field_us_value in the form_state array for this node form.

I looked at the structure of the form and a simple change like this gave me access to the field:

form_set_value($form['field_us_value']['und'][0]['value'], $us_calculated, $form_state);

And, especially do not forget ['und'] in Drupal 7 or it won't work.

ocamp’s picture

I dont know a whole lot about coding.

But just a though I had.

- You've got two fields that are required.
- But a user might not necessarily fill in both fields.
- So you need the other one to be auto completed before the form is submitted?

Cant you just give the fields a default value, like 0.

That way a user fills in one field, the others automatically filled, so the form can validate. then the fields can be calculated after submission?