Unable to set field collection field value on form validation The question might look similar to How to form_alter fields attached to an unlimited field collection? but I am more concerned about "values not saved after custom validation"

I have a field collection "field_assignments" consists of:


field_check_user: A user reference field
field_assigned_booklets: An integer field 
field_total_cost: A hidden field (used in validation handler for calculation)


I have content type "marking" having this field collection (unlimited values) along with a field_rate. I have alter node editing form and defined a custom validation which is working fine. The problem is that I cannot update the field collection item "field_total_cost" value in custom validation function. The validation function is 

function _additional_validate($form, &amp;$form_state) {
   // If rate is set to 100 and assigned booklets are 5; this should result 500
   $tc = $form_state['values']['field_assignments']['und'][0]
         ['field_assigned_booklets']['und'][0]['value'] * $form_state['values']['field_rate']['und'][0]['value'];

   $form_state['values']['field_assignments']['und'][0]['field_total_cost']['und'][0]['value'] = $tc;
}


If I dpm $form_state, I can see that the ['field_total_cost']['und'][0]['value'] is updated (see image below) but when the node is submitted, it does not show that value in node view and does not save that value in the database. How can I save a value in field collection field?



I tried form_set_value() but couldn't understand how to set value for compounded fields (field collection in this case, I'll be thankful if someone can show how can I construct that "#parent" stuff with these fields). The code with form_set_value() I tried:

 // testing on a single field - works perfectly
 form_set_value($form['field_rate']['und'][0]['value'], $tc, $form_state);

 // does not update the value
 form_set_value($form['field_assignments']['und'][0]['field_total_cost']['und'][0]['value'], $tc, $form_state);


P.S: I have written 'und' instead LANGUAGE_NONE for ease
Use Computed field instead of doing it yourself! Don't use _validate, just use _compute.


Change field_total_cost to a computed field, saving as decimal.
Create a custom module and add the following (much clearer than using Computed Fields settings inputs - yuk)


Do something like the following :

/**
 * Computed field for - field_total_cost
 */
function computed_field_field_total_cost_compute(&amp;$entity_field, $entity_type, $entity, $field, $instance, $langcode, $items) {
  $tot   = 0;
  $books = 0;
  $rate  = 0;
  if (isset($entity-&gt;field_assigned_booklets[LANGUAGE_NONE][0]['value'])) {
    $books = $entity-&gt;field_assigned_booklets[LANGUAGE_NONE][0]['value'];
  }
  if (isset($entity-&gt;field_rate[LANGUAGE_NONE][0]['value'])) {
    $rate = $entity-&gt;field_rate[LANGUAGE_NONE][0]['value'];
  }
  $tot = $books * $rate;
  $entity_field[0]['value'] = number_format((float) $tot, 2, '.', '');
}

/**
 * Display for - field_total_cost
 */
function computed_field_field_total_cost_display($field, $entity_field_item, $entity_lang, $langcode, $entity) {
  return '£' . number_format($entity_field_item['value'], 2);
}


I obviously don't know what your calculations are like but you get the idea.
This will calculate and populate the Total Cost field per field collection.

OR

If you still want to do it on validate you (afaict) can't do it on node_validate but you can on a field validate. So you need to add a custom validate function to your Field Collection field, then it should work as you think.

Add a custom validate callback on hook_field_widget_form_alter

/**
 * Implements hook_field_widget_form_alter().
 */
function MYMODULE_field_widget_form_alter(&amp;$element, &amp;$form_state, $context) {
  // Add custom validation hooks for Field Collections.
  if ($context['field']['field_name'] == 'field_assignments') {
    array_unshift($element['#element_validate'], 'MYMODULE_field_assignments_validate');
  }
}


Create the custom validate function and set the value similar to how you suggested

/**
 * Custom validate function for field_assignments.
 *
 *  @see MYMODULE_field_widget_form_alter()
 */
function MYMODULE_field_assignments_validate($element, &amp;$form_state, $form) {    
  form_set_value($form["field_assignments"][LANGUAGE_NONE][0]['field_total_cost'], array(LANGUAGE_NONE =&gt; array(0 =&gt; array('value' =&gt; 123.456))), $form_state);
}


I obliviously left out any logic in this example, but you get the idea :)