Drupal: show/hide location fields with module Conditional Fields
November 2014.
Situation
You're using module Conditional Fields to show/hide a few fields depending on another field value. You notice that everything works except your location fields, which don't respond to any trigger.
Example: Let's say you have the following profile2:
Field | Type | Widget | Misc |
---|---|---|---|
field_champ1 | List(text) | Select list | Values: "Valeur 1", "Valeur 2" or "Valeur 3" |
field_champ2 | Location | Location field | |
field_champ3 | Text | Text |
You set up the following conditional interactions:
- field_champ2 is only visible when field_champ1 has value "Valeur 2"
- field_champ3 is only visible when field_champ1 has value "Valeur 1"
Then, you open an edit form of this profile, and notice that only the second interaction works.
Finding what cause the problem
Let's dig into the code.
In
modules/conditional_fields/conditional_fields.module
, function conditional_fields_element_after_build(...)
seems to be the one that attach the dependencies from the form.
if (isset($dependencies['dependents'][$field['#field_name']])) {
foreach ($dependencies['dependents'][$field['#field_name']] as $id => $dependency) {
if (!isset($form['#conditional_fields'][$field['#field_name']]['dependees'][$id])) {
conditional_fields_attach_dependency($form, array('#field_name' => $dependency['dependee']), $field, $dependency['options'], $id);
}
}
}
// Attach dependee.
// TODO: collect information about every element of the dependee widget, not
// just the first encountered. This bottom-up approach would allow us to
// define per-element sets of dependency values.
if (isset($dependencies['dependees'][$field['#field_name']])) {
foreach ($dependencies['dependees'][$field['#field_name']] as $id => $dependency) {
if (!isset($form['#conditional_fields'][$field['#field_name']]['dependents'][$id])) {
conditional_fields_attach_dependency($form, $field, array('#field_name' => $dependency['dependent']), $dependency['options'], $id);
}
}
}
Debugging shows that our location field does not validate the preceding condition:
// Some fields do not have entity type and bundle properties. In this case we
// try to use the properties from the form. This is not an optimal solution,
// since in case of fields in entities within entities they might not correspond,
// and their dependencies will not be loaded.
if (isset($field['#entity_type'], $field['#bundle'])) {
$entity_type = $field['#entity_type'];
$bundle = $field['#bundle'];
}
elseif (isset($form['#entity_type'], $form['#bundle'])) {
$entity_type = $form['#entity_type'];
$bundle = $form['#bundle'];
}
else {
return $element;
}
This means that our field was not build with a reference to its profile2 entity.
These field form entries are built by
field_multiple_value_form(...)
in modules/field/field.form.inc
:
$element = array(
'#entity_type' => $instance['entity_type'],
'#entity' => $form['#entity'],
'#bundle' => $instance['bundle'],
'#field_name' => $field_name,
'#language' => $langcode,
'#field_parents' => $parents,
'#columns' => array_keys($field['columns']),
// For multiple fields, title and description are handled by the wrapping table.
'#title' => $multiple ? '' : $title,
'#description' => $multiple ? '' : $description,
// Only the first widget should be required.
'#required' => $delta == 0 && $instance['required'],
'#delta' => $delta,
'#weight' => $delta,
);
if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) {
...
Hum, once build, it's passed into another function. Let's find it.
It's
location_cck_field_widget_form(...)
in modules/location/contrib/location_cck/location_cck.module
:
$element = array(
'#type' => 'location_element',
'#has_garbage_value' => TRUE,
'#value' => '',
'#title' => t($instance['label']),
'#description' => t($instance['description']),
'#required' => $instance['required'],
'#location_settings' => $settings,
'#default_value' => $location,
);
return $element;
Since the returned element doesn't care about what was passed to it, the entity elements are indeed missing.
Working around the problem
One solution would be to patch module
location
.Another one is to alter the form and add the missing entries manually.
function my_module_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
if ($entity_type == 'profile2' && $entity->type == 'profile_debug') {
$form['field_champ2']['#entity_type'] = $entity_type;
$form['field_champ2']['#bundle'] = $entity->type;
if (isset($form['field_champ2'][LANGUAGE_NONE])) {
$form['field_champ2'][LANGUAGE_NONE]['#entity_type'] = $entity_type;
$form['field_champ2'][LANGUAGE_NONE]['#bundle'] = $entity->type;
}
}
}
That's it: location field
field_champ2
now behaves correctly.