form_set_value

5 form.inc form_set_value($form, $value)
6 form.inc form_set_value($form_item, $value, &$form_state)
7 form.inc form_set_value($element, $value, &$form_state)
8 form.inc form_set_value($element, $value, &$form_state)

Change submitted form values during the form processing cycle.

Use this function to change the submitted value of a form item in the validation phase so that it persists in $form_state through to the submission handlers in the submission phase.

Since $form_state['values'] can either be a flat array of values, or a tree of nested values, some care must be taken when using this function. Specifically, $form_item['#parents'] is an array that describes the branch of the tree whose value should be updated. For example, if we wanted to update $form_state['values']['one']['two'] to 'new value', we'd pass in $form_item['#parents'] = array('one', 'two') and $value = 'new value'.

Parameters

$form_item: The form item that should have its value updated. Keys used: #parents, #value. In most cases you can just pass in the right element from the $form array.

$value: The new value for the form item.

$form_state: The array where the value change should be recorded.

Related topics

8 calls to form_set_value()

File

includes/form.inc, line 1331

Code

function form_set_value($form_item, $value, &$form_state) {
  _form_set_value($form_state['values'], $form_item, $form_item['#parents'], $value);
}

Comments

Code Example

Having fought with this for a couple hours now, I thought I'd leave notes of what I found here.
Before I begin:
http://drupal.org/node/160160 is unclear, but is the top result after this page on search engines due to length. This page may need to be updated if the proper documentation can not be understood from this page.

When using drupal 6, this is the process you must use:

Optional

<?php
function hook_form(){
//... Previous Code

// You *can* (and should) declare a placeholder for the value to be inserted into later.
$form['placeholder'] = array(
'#type' => 'value'
,'#value' = > NULL // Placeholder value. Meaningless
);
//... Other Code
}
?>

Required

Because the form_set_value function requires more arguments than standard hook_validate functions provide, you must use hook_form_alter to do custom validation for your form:

<?php
function hook_form_alter(&$form, $form_state, $form_id){
if (
$form_id == '{node_type}_node_form') {
 
$form['#validate'][] = 'hook_validate_custom';
}
?>

With the form set to validate through your new function, create it. Note that $form_state must be a reference.

<?php
hook_validate_custom
($form, &$form_state){
// ... Validation Code
form_set_value( array('#parents' => array('array_key_parent', 'array_key_to_replace')) , $value, $form_state);
// ... Other Code
}
?>

Sorry for the inline array.
Here's a breakdown of the three parameters you must pass in:

  1. An array with one item: '#parents'. '#parents' is another array. '#parents' should be the arrays keys needed to reach the form value you want to replace. For instance:
    <?php
    $first_parameter
    = array(
    '#parents' => array(
     
    'first_key_in_path'
     
    ,'second_key_in_path'
     
    ,'key_to_replace' // Always the last one. *Will* create if it doesn't exist.
    )
    );
    ?>
  2. The value you want inserted.
  3. $form_state

Another example

I banged my head against this same problem for about an hour. Here's how I solved it.

What I wanted to do

I needed to hide the default "Title" field and automatically populate it using two CCK fields: "First name" and "Last name." The value of the "Title" field would be: "[First name] [Last name]."

How I did it

First, I had to implement hook_form_FORM_ID_alter() for the "profile" content type's node form:

<?php
function mymodule_form_profile_node_form_alter(&$form, &$form_state) {
 
// Hide the extraneous title ("Name") field
 
$form['title']['#type'] = 'hidden';

 
// Add placeholder for #value
 
$form['title']['#value'] = NULL;

 
// Add a validation function that will fill in the value of the title ("Name") field
 
$form['#validate'][] = 'mymodule_profile_node_form_validate';
}
?>

In this case, the placeholder value was required. The form would throw this error otherwise: "The Title field is required."

Second, I implemented the validation form I assigned in hook_form_FORM_ID_alter() above:

<?php
function mymodule_profile_node_form_validate($form, &$form_state) {
 
$name = $form_state['values']['field_profile_firstname'][0]['value'] . ' ' . $form_state['values']['field_profile_lastname'][0]['value'];
 
form_set_value($form['title'], $name, $form_state);
}
?>

In this case, I was able to pass form_set_value() the form element itself: $form['title']. Passing it a nested array, as described in the above comment, failed.

I came across the same

I came across the same problem however hook_form_alter() has it's own purpose and if we are using it to make something work in forms that the documentation says should work without specifically mentioning hook_form_alter(), odds are good that we are simply missing something we should be doing.

After much research starting with this premise here's what you probably should be doing to make form_set_value() work from a validate routine (comments that are not normal to the coded but have been added to help understanding are IN ALL CAPS):

<?php
function yourmodule_yourform_validate($form, &$form_state) {
  if (
'your test condition here') {
   
form_set_error('yourfield', t('Your message')); # <-- OPTIONAL, IF YOUR TEST IS NOT PICKING UP AN ERROR CONDITION, OBVIOUSLY DON'T DO THIS.
   
form_set_value($form['yourfield'], 'some value', $form_state);
  
$form_state['rebuild'] = TRUE; # <-- THIS IS CRITICAL OTHERWISE YOUR FORM WILL NOT GET REBUILT TO INCORPORATE YOUR CHANGES

        # IF YOU HAVE A NEED TO MAKE YOUR OWN CHANGES BUT WITHOUT OVER WRITING THE USER'S INPUT THEN INSTEAD OF USING form_set_value YOU COULD PROBABLY USE THE STORAGE LIKE THIS:
       
$form_state['storage']['my thing to store'] = 'something';
  }
}

function
yourmodule_somefunction_that_starts_your_form() {
   
$output .= drupal_get_form('yourmodule_yourform');
  return
$output;
}

function
yourmodule_yourform(&$form_state = array()) { # <-- THIS IS CRITICAL, YOU MUST PICK UP $form_state OTHERWISE YOU CANNOT GET YOUR CUSTOMIZATIONS OR THE USER'S INPUT AND THEREFORE ALL AS "$form_state['rebuild'] = TRUE" USED IN THE VALIDATE ROUTINE WILL DO IS RE-INITIALIZE THE FORM

   
$form['mb'] = array(
       
'#type' => 'fieldset',
       
'#title' => t('My Administration'),
       
'#collapsible' => TRUE,
       
'#collapsed' => FALSE,
    );

   
$form['mb']['name'] = array(
       
'more stuff like above for this field too'
   
);

   
yourmodule_yourform_restore_user_input($form, $form_state);
   
$form_state['rebuild'] = FALSE;
    return
$form;
}

function
yourmodule_yourform_restore_user_input(&$form, $form_state) {
   
   
# DON'T DO ANYTHING IF THE FORM IS NOT BEING REBUILT
   
if (!is_array($form_state['values']) || count($form_state['values']) < 1 || empty($form_state['rebuild'])) return;
   
   
# Your code for restoring the user's input.
    # This will copy data over from $form_state which contains the last POST variables in
    # $form_state['values'] to $form'.  Exactly how you do this will be specific to you. It will
    # probably also be dependent on whether you set the "tree" option.  I personally had
    # simple needs and was able to use a recursive routine that simply matched up array
    # keys (recursive because I used fieldsets and therefore had a hierarchy in $form).
}
?>

Some useful details

Note that:

  • If you set errors on a form then rebuild will be ignored
  • If you set storage on a form then rebuild true is implied regardless of whether you've set it or not.
  • There is an example of using storage, page rebuilding and multi-part forms here: http://drupal.org/node/717750
  • If you're writing your own form from scratch, there really is no need to use hook_form_alter that I can see.

As is discussed in the documentation, rebuild is useful when you want to do something like reconfigure address fields for a form based on the country.
It's somewhat inconvenient that form errors prevent rebuilds, though it is possible to clear your errors if you have (or might have) set some and then decide you want to rebuild instead. I'm not sure why this condition is applied on rebuilds, but I suspect it is intended to prevent problems where Drupal gets confused about how the form got rebuilt (and how to reset the error state) - as presumably displaying form errors is a kind of rebuild operation.

I studied the source for the form handling API when I had some problems getting my form to work, but some details of how parameters are passed to one function or another mean it's overly time-consuming to figure out exactly what's happening. I guess I didn't really have time to work out exactly how caching was working either - but from what I could see, extensive reconfiguration of forms, or 'helping' with user input are an area where it may often be better to rely on javascript (with or without AJAX) if you think you can get away with it.

It's pretty obvious how you could do most of these input field modifications easily and responsively in client-side script and write the server side so it just checks that what comes in is valid. Like with separating presentation and underlying content there are obvious maintainability advantages. The only downside is the lack of certainty over client side script support, but I guess that is becoming less and less of an issue all the time.

Another code example ...

I needed to populate (CCK) field B with the value of (CCK) field A during form validation. Field B is part of a fieldset.

form_set_value(array('#parents' => array('field_a', '0', 'value')), $form_state['values']['field_b'][0]['value'], $form_state);

CCK filefield

It works slightly different for a CCK filefield for D6 since there is no 'value' attribute for filefields:

$item = array(
  '#parents' => array(
    'field_filefieldname',
    '0',
  ),
);

// $file is a fully populated file object (see {files} table for structure)
// The object must be cast as an array
form_set_value($item, (array)$file, $form_state);

Login or register to post comments