views:

855

answers:

3

I am writing a drupal module that involves a form with many checkboxes. E.g.

$form['myform_checkboxes'] = array('#type' => 'checkboxes', ...)

I have made the key for these checkboxes numeric, starting with 0. E.g.

$form['myform_checkboxes']['#options'][0] = '0:00';
$form['myform_checkboxes']['#options'][1] = '1:00';

Upon implementing the myform_checkboxes_submit function, I have discovered that it is difficult to interpret what the user's input was. On the interwebs, I found a few nice lines of code that did what I needed.

$checked = array_intersect(
  array_keys($form_state['values']['myform_checkboxes']),
  array_values($form_state['values']['myform_checkboxes'])
);

This seems to work great; the $checked variable is an array including only the checked checkboxes. The only problem is that the value 0 (representing the 0th checkbox) is always included in $checked, whether it was actually checked or not.

Also useful to note: the zero appears first in the list if it is was checked, but last if it is not.

What would be the best way to resolve this situation, assuming that changing the indexing of the checkboxes were out of the question? (Related bonus question: is there an easier way to get the user's checked boxes from the drupal form variables?)

A: 

You could use a JS submit handler to check the checked state of the checkboxes and put the values into a hidden field.

You would then need to use a Drupal form handler to decode the values on the other end.

Jeremy French
+3  A: 

As the value returned by an unchecked checkbox is 0, there is no way to recognize the checked state if you use 0 as the return value for that also. So the immediate answer to your question is that there is no best way, simply because there is no way (apart from a js workaround as suggested by Jeremy, which would be a pretty complicated solution to a simple problem).

So if you need your result array to start with index 0, you will have to get rid of the 0 entry temporarily when building the form options, putting it back in after extracting the results. An easy way to do this would be to use -1 (or any other value not used in the rest of the array) as a placeholder for 0, replacing it again after extracting the checked values.

Another obvious solution would be reindexing, as mentioned by Jeremy. Looking at your example, why don't you just use your display values (0:00, 1:00, ...) as keys/return values also? No ambiguity there, and easy to convert to integers, if need be.

Henrik Opel
Just the information I needed. I preferred using the integers as keys because of the processing that I was to perform later. Though they would be easy to convert, the display values will (eventually) be dynamic. Changing the 0 key to -1 and back is probably what I will end up doing.
Dan
+1  A: 

Actually, what I ended up doing was this.

if (in_array(0, $checked) && $checked[0] != 0) {
  unset($checked[count($checked) - 1]);
}

It checks if 0 is in the array, and if it's not the first item, then it must be the last (this happens when the user doesn't check the box corresponding to 0). So it removes that item from the array, since it was not checked. Not ideal or pretty, but it made sense for my situation.

In most other situations (and perhaps in mine), as has been pointed out, re-indexing would be better.

EDIT: for anyone who cares, this is the helper function I ended up creating for myself (comments included).

function _mymodule_get_checked_checkboxes(&$form_state, $table) {

  // Discover which boxes were checked.
  $checked = array_intersect(array_keys($form_state['values'][$table]),
                          array_values($form_state['values'][$table]));

  // The key '0' is included in the first position if it was selected,
  //  and in the last if it was not.
  //  this is how checkboxes return their data.
  //  However, we don't want 0 to be in the array
  //  therefore, we remove it if 0 is found to be in the last position
  $num_checked = count($checked);
  if ($checked[0] != 0 && $checked[$num_checked - 1] == 0) {
    unset($checked[count($checked) - 1]);
  }
  // It also happens if nothing is selected.
  // In the case that only 0 is selected, assume otherwise.
  else if ($num_checked == 1 && $checked[0] == 0) {
    unset($checked[0]);
  }

  sort($checked);
  return $checked;
}
Dan