I am working on a special cck field type, "See Map" links for content types that have mappable addresses, and saving the value of a checkbox widget does not work.

The field configuration allows you to specify the names of the other fields that represent the street address, the city etc. The widget that is shown when the content is edited is a simple checkbox, to enable or disable these "See Map" links for this particular node. The value of this checkbox is not saved to the database, and I have no clue why.

I read the cck documentation here, under Handbooks. Also, I looked at several other cck field implementations.

It would be great if someone could have a quick look at the source code and give me some hints.

The latest development version can be downloaded from:
http://drupal.org/project/see_map

Thanks,
Marius

Comments

mariuss’s picture

Here is the widget related code:


// hook_widget_info
function see_map_widget_info() {
  return array(
    'see_map_checkbox' => array(
      'label' => '"See Map" links',
      'field types' => array('see_map'),
    ),
  );
}

// hook_widget_settings
function see_map_widget_settings($op, $widget) {
  switch ($op) {
    case 'validate':
      if ($widget['required'] == 1) {
        form_set_error('required', t('See Map field cannot be required!'));
      }
      if ($widget['multiple'] == 1) {
        form_set_error('multiple', t('See Map field cannot have multiple values!'));
      }
      break;
  }
}

// hook_widget
function see_map_widget($op, &$node, $field, &$items) {
  switch ($op) {
    case 'form':
      $form = array();
      
      $default_value = 0;
      if (isset($field['widget']['default_value'][0]['show_see_map'])) {
          $default_value = $field['widget']['default_value'][0]['show_see_map'];
      }
      
      $form[$field['field_name']][0]['show_see_map'] = array(
        '#type' => 'checkbox',
        '#title' => t('Show "!seemap" links.', array('!seemap' => $field['widget']['label'])),
        '#default_value' => isset($items[0]['show_see_map']) ? $items[0]['show_see_map'] : $default_value,
        '#required' => FALSE,
        '#description' => $field['widget']['description'],
      );
      
      return $form;
  }
}

mooffie’s picture

$form[$field['field_name']][0]['show_see_map'] = array(
'#type' => 'checkbox',

You forgot '#tree' => TRUE.

(And you can remove the $field['widget']['default_value'] thingies. CCK does this for you automatically.)

mariuss’s picture

Just tried adding the suggested #tree property, it did not make any difference.

Also, reading the forms api reference, I can't see how #tree applies to 'checkbox'. It is not listed as a valid property (only on the top, maser table, and there probably it is a typo). It is a valid property for 'checkboxes' (plural), but even there the default value is TRUE.

Am I missing something?

Any other hints?

Thanks,
Marius

mooffie’s picture

Sorry I wan't clear. You're right, there's no point in putting the #tree on the checkbox.

You must put it on the field's top-most element:

$form[$field['field_name']]['#tree'] = TRUE;

mariuss’s picture

Thanks a lot mooffie. Adding #tree at the top level fixed the issue. It is working fine now.

So, this is pure forms api stuff? Or something specific for widgets?

Thanks again,
Marius

mooffie’s picture

So, this is pure forms api stuff? Or something specific for widgets?

When you don't use #tree, the form API "flattens" all form elements so that only the "leaf" elements are seen. Your original code is identical to a form which contains a top-level "show_see_map" checkbox and nothing more. When this form is submited, Drupal sees:

$node->show_see_map;

The hierarchy, the ancestry, is forgotten, obliterated. CCK doesn't even know this detached piece of data belongs to some field.

However, when you add #tree, Drupal now sees:

$node->field_map_toggler[0]['show_see_map'];

Now the field value(s) is formed correctly, and now CCK knows to relate this data to the field --to a field it knows everything about.

(And remmemebr to remove the lines dealing with the 'Defailt Value' --I don't see the reason for their existence.)

mariuss’s picture

It starts to make sense.

I looked at other widgets, and none of them specified '#tree'. How do they work?

In the example you give, where is the "map_toggler" part coming from?

mooffie’s picture

I looked at other widgets, and none of them specified '#tree'.

All widgets I have ever looked at do specify #tree :-)

You probably missed it. Use the 'search' command of your editor.

In the example you give, where is the "map_toggler" part coming from?

Oh, that. That's the name the administrator chose for this field (on the q=admin/content/types/story/add_field page). He could have chosen any other name. "Map Toggler" just looked good to me.

Had you talked about a generic field for numbers, I would probably have chosen to name this field "Age".

Every field has a name. To be precise, every instance of a field has a name. When you build the form, it's under this name that you put all elements.

mariuss’s picture

You are absolutely right, sorry, I should have checked before I posted. That was a huge blind spot on my side.

The map_toggler part is clear as well now.

Thanks for all the help and clarifications.

mariuss’s picture

And remmemebr to remove the lines dealing with the 'Defailt Value' --I don't see the reason for their existence.

I removed the code related to #default_value, but now the default value selected when the field is configured is not set anymore. The checkbox is always unchecked on new content.

When you edit the content type, on the Manage fields tab, if you click configure for a See Map type field (or any field for this example), in the Widget settings field set, at the bottom, there is a nested and collapsed field set called Default value. Whatever you set here is supposed to be a default. But for that to work you do need the code that I have around #default_value.

At least for me this is the only way it works.

mariuss’s picture

Can someone point me to some more detailed documentation or other modules that do a similar thing (saving checkbox values)?

Any hints?

dwees’s picture

Whenever I am having trouble with a form element and it not being saved in the database I have had two things that I have found to be true.

1. I have used variable_get('form_element_name', form_element_default_value) and 'form_element_name' has not matched the form element in question.
2. The type I'm using for my default value doesn't match the type the form element is expecting.

Instead of 0, try the not-so-obvious array(0->0).

With checkboxes (plural) I think this allow Drupal to map each title of the checkbox to the value of the form element, not sure if it will work for a single checkbox or not.

I think you'd have the same problem with select, and radio type form elements as well.

Dave