Hello.
Just starting out doing some actual drupal dev and I've got a real noob question. I'm trying to get a multi-select form field to redisplay it's values after it's been saved. This is for an admin settings page and it's nothing spectacular, just a learning exercise.

My Code:

<?php
function mymod_admin_settings()
{
    
$admin_form=array();
    
//Build an associated node type list, easy enough.
    
$node_list = array();
    
$result = db_query("SELECT type, name FROM {node_type} ");
     while(
$row = db_fetch_object($result))
     {
     
$node_list[$row->type]=$row->name;
     }
    
//use node_list as options list
    
$admin_form['mymod_nodetype'] = array(
        
'#type' => 'select',
        
'#title' => t('Select Node Types'),
        
'#options' => $node_list,
        
'#multiple' => TRUE,
        
'#required' => TRUE,
        
'#description' => t('Select the node types'),
        
'#default' => variable_get('mymod_nodetype',NULL),
    );
    return
system_settings_form($admin_form);
}
?>

Up to this point, the menu hook and all that has worked perfectly. I get to this page and see the setting with no problems. I know that the settings are being saved because if I go: print_r(variable_get('mymod_nodetype',NULL)); I end up with the correctly valued variable, which is an array. But how do I get them to be highlighted since the '#default' doesn't seem to be picking it up?

Any help would be appreciated.
Many Thanks!

Comments

So I found out that when the

So I found out that when the array returns from it's saved state (the variable get array) it is saved in a way that it not the original array of [key]=>value, but it is remapped and saved as [key]=>key. So the key value is being used to store the key, where as I'm a more user friendly display as the value.

An example print_r() output of the aforementioned $form array:

Array
(
    [mymod_nodetype] => Array
        (
            [#type] => select
            [#title] => Select Node Types
            [#options] => Array
                (
                    [page] => Page
                    [news] => News
                    [blog] => Blog
                    [mycontenttype] => My Custom Content Type
                )

            [#multiple] => 1
            [#required] => 1
            [#description] => Select the node types
            [#default] => Array
                (
                    [news] => news
                    [mycontenttype] => mycontenttype
                )

        )
)

I assume that this is Drupal's default behavior since it is probably calling drupal_map_assoc on an already associated array. Why would it do this? I don't know. Annoying for trying to save a custom array of values. Is there a way around this to get this to work? I'm doing some more searching and if I find any answer I will post it. I personally think this is an overlooked bug, but I'm sure it's probably considered more of a 'feature'.

Your help is greatly appreciated.
Thanks.

Fixing array didn't work

So in further investigation, If I write a bit of code to fix the saved array to match the created options array, it still is not rendered correctly. The function I used for this was:

<?php
function fixsavedarray(&$item,$key,$list_array)
{
    if(
array_key_exists($key,$list_array)){
       
$item=$list_array[$key];
    }
}

//Then added this in to the aformentioned function
//right above the line: $admin_form['mymod_nodetype'] = array(

$selected_nodes= variable_get('mymod_nodetype',NULL);
array_walk($selected_nodes,'fixsavedarray',$node_list);

//Then replace '#default' in the $form array with:
'#default' => $selected_nodes
?>

The arrays now match in [key]=>value, but it didn't solve anything.

A roundabout way of doing things (SOLVED)

So in a roundabout way of doing things I've made this work using the '#post_render' option in the form. I then use a search and replace on the $content element for where the value='[key]' and insert a selected='selected' where there is a match. Of course I've broken out the retrieval of the 2 arrays into a separate function and this is no longer a pretty as I'd hoped it would have been, but hopefully it helps someone out who's also having Drupal nightmares.

My final (working) code:

<?php
function mymod_fixsavedarray(&$item,$key,$list_array)
{
    if(
array_key_exists($key,$list_array)){
       
$item=$list_array[$key];
    }
}

function
mymod_prender($content, $elements)
{
   
$list=array();
   
mymod_getlistarrays($list);
    foreach(
$list as $name => $arr){
        if(
substr_count($name,'selected_')>0){
            foreach(
$arr as $key => $val){
               
$find_value = '<option value="'.$key.'">'.$val.'</option>';
               
$replace_value = '<option value="'.$key.'" selected="selected">'.$val.'</option>';
               
$content = str_replace($find_value,$replace_value,$content);
            }
        }
    }
    return
$content;
}

function
mymod_getlistarrays(&$list)
{
       
$node_list = array();
     
$result = db_query("SELECT type, name FROM {node_type} ");
        while(
$row = db_fetch_object($result))
      {
         
$node_list[$row->type]=$row->name;
      }

     
$selected_nodes = variable_get('mymod_nodetype',NULL);

     
array_walk($selected_nodes,'mymod_fixsavedarray',$node_list);

     
$list['nodes']=$node_list;
     
$list['selected_nodes']=$selected_nodes;
}


function
mymod_admin_settings()
{
   
$list=array();
   
mymod_getlistarrays($list);

   
$form['mymod_nodetype'] = array(
        
'#type' => 'select',
        
'#title' => t('Select Node Types'),
        
'#options' => $list['nodes'],
        
'#multiple' => TRUE,
     
'#required' => TRUE,
        
'#description' => t('Select the node types'),
       
'#default_value' => $list['selected_nodes'],
       
'#post_render' => array('mymod_prender'),
    );
      return
system_settings_form($form);
}
?>

If anybody comes up with a better solution, please do tell.

I love answering my own questions, but hey at least I'm learning something and don't have to thank anybody for their help.

#default should have been

#default should have been #default_value.

Interested in hiring us? jaypan.com

I did catch that, it was a

I did catch that, it was a typo from the copy/paste and change of mod name to protect the innocent. It made no difference, and I fixed it in last post, but the issue is(was) still the same.

#default_value should be defined before #options

The form element definition should be

$admin_form['mymod_nodetype'] = array(
  '#type' => 'select',
  '#title' => t('Select Node Types'),
  '#default_value' => variable_get('mymod_nodetype',  array()),
  '#options' => $node_list,
  '#multiple' => TRUE,
  '#required' => TRUE,
  '#description' => t('Select the node types'),
);

The order doesn't matter.

The order doesn't matter. That's the whole point of the keyed array.

Interested in hiring us? jaypan.com

I tried the above code on my

You're right, the order does not matter, though I tried the code I posted on my test site and it works.

Did I miss this in the docs?

I did try this, and you are correct, this does seem to work.

If you take the original code I posted, and place the default values first, it works.
I don't understand quite how (or why) Drupal is rendering this in such a fashion that it matters which order these are in, since the form itself is an associated array (the whole point of that type of array), but it does.
An explanation would be nice, but I've wasted enough of everybody's time (including my own) with this.

Here (again) is the full thing with the default values first. This is working.

<?php
function mymod_admin_settings()
{
    
$admin_form=array();

    
//Build an associated node type list, easy enough.
    
$node_list = array();
    
$result = db_query("SELECT type, name FROM {node_type} ");
     while(
$row = db_fetch_object($result))
     {
     
$node_list[$row->type]=$row->name;
     }

    
//use node_list as options list
    
$admin_form['mymod_nodetype'] = array(
        
'#type' => 'select',
        
'#title' => t('Select Node Types'),
       
//Note that here we add the default values from the stored variable FIRST
        
'#default_values' => variable_get('mymod_nodetype',array()),
       
//now we add our associated array of options
        
'#options' => $node_list,
        
'#multiple' => TRUE,
        
'#required' => TRUE,
        
'#description' => t('Select the node types'),
    );
    return
system_settings_form($admin_form);
}
?>

Wasn't that was a whole lot easier? I think it was. Thanks Prakash!

One thing to keep in mind is that the saved array, is still not an the original associated array, but an associated array of the array keys (see 1st comment).

Now to mention this in the form API for future users to learn.

nobody click here