I am validating a form containing text in a textarea field. I have found that when my validation function is called the data in the form's textarea field is not in $form_state (or whatever the second argument's name in the validation function definition is) as expected. The form's textarea key is in $form_state as a subkey of a values key, but the the value associated with that subkey is a zero length string. Instead I find the expected text is in the $_POST variable and its key is named after the submitted form's textarea's key.

From all I have read the text is supposed to be in $form_state. All the examples I have seen use the $form_state variable to get this data instead of from $_POST. I would like my code to get if from $form_state as this appears to be the proper way to do it. What could cause the text in the textarea field not to appear in there?

Comments

jordojuice’s picture

Absolutely the value in validation callbacks should be in $form_state['values']. It's impossible to tell what the problem could be without seeing your form and validation callbacks. Generally, a validation callback should look something like:

function mymodule_form_myform_validate($form, &$form_state) {
  if ($form_state['values']['my_field'] != 'desired value') {
    form_set_error('my_field', t('You have entered an invalid value.'));
  }
}

Make sure the function signature is correct. Form values can always be found in $form_state['values']. If you want more insight please post your complete form and validate functions so we can check it out.

spflanze’s picture

I have just noticed at:
http://api.drupal.org/api/drupal/developer--topics--forms_api_reference....
there are two keys that seem to do the same thing. These are #validate and #element_validate. What is different between them?

I am using #validate in the textarea field's array because this textarea field has its own submit button and when it is clicked on I want only to validate this field. I will try #element_validate and if this does not make a difference I will post code.

jordojuice’s picture

The difference between the two keys is that they are intended for different items. Generally #validate should go in $form['#validate'] and will validate every item in the form. #element_validate is meant to be part of a specific form element and will server to validate that element upon submit. it's usually used to do things like validate that a specific form value is an integer with a commonly used integer validation function. If you want to have separate submits on your form then you may need to use separate form functions all together. Then, in your form builder concatenated them together

$output = drupal_get_form('mymodule_first_form');
$output .= drupal_get_form('mymodule_second_form');
return $output;

Then, on each form use $form['#validate'] = array('mymodule_validate_callback');. That way you can separate the validation of the form that contains the textarea and submit from whatever other form exists on the same page. I could get more specific with the code your actually working with if you post it, but that's the general idea.

spflanze’s picture

Using #element_validate instead of #validate did not make a difference. So here is the code. The trouble is in function _db_prefixes_add_validate() of file ncludes/db_prefixes.table_names.inc (second file below).

File db_prefixes.module:

<?php
// $id$

/**
 * @file
 * An aid to managing database prefixes.
 */

function db_prefixes_perm(){
  return array('administer db prefixes');
}

function db_prefixes_menu(){
  $items["admin/build/prefixes"] = array(
    'title' => 'Manage DB Table Prefixes',
    'description' => 'Displays database table names. Creates copy and paste-able code to paste into the db_prefix arrays',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('db_prefixes_table_names'),
    'access callback' => 'user_access',
    'access_arguments' => array('administer db prefixes'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'includes/db_prefixes.table_names.inc',
    );
  $items["admin/build/prefixes/tables"] = array(
    'title' => 'Tables & Dependencies',
    'description' => 'Displays database table names.',
    'access callback' => 'user_access',
    'access_arguments' => array('administer db prefixes'),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'includes/db_prefixes.table_names.inc',
    );
  $items["admin/build/prefixes/prefixes"] = array(
    'title' => 'Prefixes',
    'description' => 'Manages the prefix list that lists which prefixes are avaialable for each site.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('db_prefixes_prefixes'),
    'access callback' => 'user_access',
    'access_arguments' => array('administer db prefixes'),
    'type' => MENU_LOCAL_TASK,
    'file' => 'includes/db_prefixes.prefixes.inc',
    );
  return $items;
}

function db_prefixes_theme()
{
  return array
  (
    '_db_prefixes_prefixes_theme' => array(
    	'arguments' => array('form' => NULL),
    ),
  );
}

File includes/db_prefixes.table_names.inc

// $id$

/** Prefix editing callbacks for database prefixes module.
*/
<?php
function _db_prefixes_add_validate( $form, &$form_state ){
  $error = '';
//  $_POST must be used here because $forn_state['Add Prefixes Text'] is empty:
  $prefixes = preg_split( '/[\s]/', $_POST['Add_Prefixes_Text'] );
  if( count($prefixes) <= 1 && $prefixes[0][1] == 0 ){
    $error = t('No prefixes have been entered to add.');
  }else{
    foreach( $prefixes as $prefix ){
      if( preg_match( '/[^a-z,A-Z,_,\.]/', $prefix, $matches ) > 0 ){
        $error .= t('<strong>' . $prefix . '</strong>' . ': Illegal character "'. $matches[0][0] . '"<BR>');
      }
      if( substr_count( $prefix, '.' ) > 1 )
        $error .= t('<strong>' . $prefix . '</strong>' . ': Only one period character allowed per prefix.<BR>');
    }
  }
  if( $error != '' ) {
    form_set_error('Add Prefixes', $error );
  }
  $form_state['rebuild'] = TRUE;
  return;
}

function db_prefixes_prefixes($type){
  $files = drupal_system_listing('^settings\.php$', 'sites', 'name', 0);
  $sites = array();
  $form = array();
  $sid = 0;
  foreach( $files as $file ){
    $sites[] = array( 'sid' => $sid++, 'site' => preg_replace('|.*?/([^/]*?)/[^/]*$|', '$1', $file->filename ));
  }

  // Temp fill an array of prefixes until they can be edited in further development:
  $prefixes = array(
  array( 'pid' => 3, 'pref' => 'pref3' ),
  array( 'pid' => 6, 'pref' => 'pref6' ),
  array( 'pid' => 20, 'pref' => 'pref20' ),
  array( 'pid' => 32, 'pref' => 'pref32' ),
  );

  foreach( $prefixes as $prefix ){
    $colHeaders[$prefix['pid']] = array( '#value' => $prefix['pref'],
    );
  }

  foreach( $sites as $site ){
    $rowHeaders[$site['sid']] = array( '#value' => $site['site'] );
    foreach( $prefixes as $prefix ){
      $form['checkboxes'][$site['sid']][$prefix['pid']] = array( '#type' => 'checkboxes',
          '#options' => array(''),
      //          '#attributes' => 'id="s' . $site['sid']. 'p' . $prefix['pid'] . '"',
          '#id' => 's' . $site['sid']. 'p' . $prefix['pid'],
      );
    }
  }

  $form['colHeaders'] = $colHeaders;
  $form['rowHeaders'] = $rowHeaders;

  $form['Add Prefixes'] = array('#type' => 'fieldset',
            '#title' => 'Add Prefixes',
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
  );

  $form['Add Prefixes']['Add Prefixes Text'] = array( '#type' => 'textarea',
                 '#default_value' => '',
                 '#rows' => 5,
                 '#description' => t('Enter prefixes to be added separated by a whitespace.' 
                 . ' Only alphanumeric, underline, and period characters are allowed.'
                 . ' Characters before a period specify a database name.'),
                 );

                 $form['Add Prefixes']['Submit'] = array('#type' => 'submit',
 		'#value' => t('Submit'),
                '#element_validate' => array('_db_prefixes_add_validate'), 
                 );

                 $form['#theme'] = '_db_prefixes_prefixes_theme';
                 return $form;
}

function theme__db_prefixes_prefixes_theme($form){
  $rows = array();
  foreach( element_children($form['rowHeaders']) as $head ){
    $row = array();
    $row[] = drupal_render( $form['rowHeaders'][$head] );
    foreach( element_children($form['checkboxes'][$head]) as $pid ){
      $row[] = drupal_render( $form['checkboxes'][$head][$pid] );
    }
    $rows[] = $row;
  }

  $header = array('');
  foreach( element_children($form['colHeaders']) as $col ){
    $row[] = drupal_render( $form['checkboxes'][$head][$pid] );
    $header[] = drupal_render( $form['colHeaders'][$col] );
  }

  $output = theme('table', $header, $rows );
  return $output . drupal_render($form);
}

File includes/db_prefixes.table_names.inc

<?php
// $id$

/** Database table name callbacks for database prefixes module.
 */

function _db_prefixes_fcmp( $a, $b ){
  if ( $a->filename < $b->filename ) return -1;
  if ( $a->filename > $b->filename ) return 1;
  return 0; // equality
}

function db_prefixes_table_names($type){
  $form = array();
  $files = drupal_system_listing('\.module$', 'modules', 'name', 0);
  usort( $files, '_db_prefixes_fcmp' );
  foreach( $files as $file ){
    if( substr($file->filename, 0, 8) == 'modules/' ){
      $ModsDir = 'modules/';
    }else{
      $ModsDir = preg_replace('%(.*?/modules/(contrib/|)).*$%', '$1', $file->filename);
    }
    $Mod = preg_replace('%' . $ModsDir . '([^/]*?)/.*$%', '$1', $file->filename);
    $install = preg_replace('%(.*?\.)[^.]*?$%', '$1install', $file->filename);
    $info = preg_replace('%(.*?\.)[^.]*?$%', '$1info', $file->filename);
    if( file_exists($install) ) {
      require_once $install;
      $hook_schema = $file->name . '_schema';
      // Test whether this is a submodule. This test depends on the $files array being sorted.
      $sub = $Mod != $file->name;

      if( function_exists($hook_schema)) {
        $schema = call_user_func( $hook_schema );
        if( !empty($schema) ){
          // Read the dependencies:
          $InfoFile = preg_replace('%(.*?\.)[^.]*?$%', '$1info', $file->filename);
          $Info = drupal_parse_info_file($InfoFile);
          // Put the table names into the form:
          if( !array_key_exists($ModsDir, $form) ) {
            $form[$ModsDir] = array('#type' => 'fieldset',
              '#title' => t($ModsDir),
              '#collapsible' => TRUE,
              '#collapsed' => FALSE,
            );
          }
          if( $sub ){
            if( !array_key_exists( $Mod, $form[$ModsDir] ) ) {
              $form[$ModsDir][$Mod] = array('#type' => 'fieldset',
                '#title' => t($Mod),
                '#collapsible' => TRUE,
                '#collapsed' => TRUE,
              );
            }
            $pform = &$form[$ModsDir][$Mod];
          }else{
            $pform = &$form[$ModsDir];
          }

          $pform[$file->name] = array('#type' => 'fieldset',
            '#title' => t($file->name),
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
          );

          $tables = array_keys($schema);
          asort( $tables );
          $N = count($tables);
          $pform[$file->name]['tables'] = array(
                 '#attributes' => array('readonly' => 'readonly'),
                 '#weight' => 0,

          );
          $pformTables = &$pform[$file->name]['tables'];
          if( $N == 1 ){
            $pformTables += array( '#type' => 'textfield', '#default_value' => $tables[0], );
          }else{
            $indexes = array_keys( $tables );
            $pformTables += array( '#type' => 'textarea',
                 '#default_value' => $tables[$indexes[0]],
                 '#rows' => $N-1,
            );
            for( $i=1; $i<$N; $i++ ){
              $pformTables['#default_value'] .= chr(10) . $tables[$indexes[$i]];
            }
          }
          
          if( !empty($Info) && !empty($Info['dependencies']) ){
            //$pformTables += array( '#title' => 'Tables', );
            $pInfoDeps = &$Info['dependencies'];
            asort($pInfoDeps);
            $pform[$file->name]['dep'] = array( '#type' => 'textfield',
                   '#title' => 'Dependencies',
                   '#attributes' => array('readonly' => 'readonly'),
                   '#weight' => 1,
                   '#default_value' => $pInfoDeps[0],
            );
            $N = count($pInfoDeps);
            $pformdepdef = $form[$file->name]['dep']['#default_value'];
            for( $i=1; $i<$N; $i++ ){
              $pformdepdef .= ' ' . $pInfoDeps[i];
            }
          }
        }
      }
    }
  }
  ksort($form);
  return $form;
}
spflanze’s picture

This problem is solved. It went away when I changed keys from 'Add Prefixes' to 'Add_Prefixes' and ''Add Prefixes Text' to 'Add_Prefixes_Text'. Is it documented anywhere that keys in forms cannot have spaces? If not it should be.

jordojuice’s picture

That's a good question! I'll check it out and certainly can update documentation if not.