Note: There is some information here that may be extraneous, because I started with a broader question. I still think the context might help so I'm leaving the detail in. Feel free to skip to The Code and The Question though, and come back for the detail.
The Detail
I have a node form that has a boolean field, and a multivalue field collection field that contains a multivalue field collection field, that contains a boolean field. Since that makes little sense, let me try to diagram it. (There are of course other fields, but these are the relevant ones)
field_mr_home_leave - boolean - single value
field_mr_fc_itineraries - field collection - unlimited values
|--- field_mr_fc_legs - field collection - unlimited values
|--- field_mr_fc_leg_home_leave - boolean - single value
I'm sorry the names here are a little confusing, please note one of the home_leave fields has fc_leg in the prefix. The home leave field deeper in the hierarchy is the ones) I want to be dependent on the value of the home_leave field at the top level.
Because conditional fields does not give me the option of making sub fields of a field collection dependent on the value of a field in the parent form (if it does, please tell me how), I want to write code in my custom module to alter the form so that when the value of field_mr_home_leave
is set to 1, field_mr_fc_leg_home_leave
is visible, and when field_mr_home_leave
is set to 0 field_mr_fc_leg_home_leave
is invisible.
In terms of the form, if the parent boolean is set to Yes (1), I want to see any boolean fields in the sub sub field collection. If it is set to No (0) they should not show up.
The Code
The form has an id of mission_request_node_form
and my module is called missions
. Here is the code I have so far:
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function missions_form_mission_request_node_form_alter(&$form, &$form_state, $form_id)
{
$form['field_mr_home_leave'][LANGUAGE_NONE]['#ajax'] = array(
'callback' => 'missions_hide_home_leave_toggle',
'wrapper' => 'itinerary-section',
'effect' => 'slide',
);
$form['field_mr_fc_itineraries']['#prefix'] = '<div id="itinerary-section">';
$form['field_mr_fc_itineraries']['#suffix'] = '</div>';
if ((!empty($form_state['values']['field_mr_home_leave']) && $form_state['values']['field_mr_home_leave'] == 1) || (empty($form_state['values']['field_mr_home_leave']) && $form['field_mr_home_leave'][LANGUAGE_NONE]['#default_value'] == 1)) {
$home_leave_toggle_access = true;
} else {
$home_leave_toggle_access = false;
}
foreach ($form['field_mr_fc_itineraries'][LANGUAGE_NONE] as $key => &$itinerary) {
if (is_integer($key)) {
foreach ($itinerary['field_mr_fc_legs'][LANGUAGE_NONE] as $key => &$leg) {
if (is_integer($key)) {
$leg['field_mr_fc_leg_home_leave']['#access'] = $home_leave_toggle_access;
}
}
}
}
$form_state['rebuild'] = TRUE;
}
function missions_hide_home_leave_toggle($form, $form_state) {
dpm('testing');
return $form['field_mr_fc_itineraries'];
}
I'm adding the #ajax array to the parent boolean field and then testing to see what the value of the field is. I then set the #access value of any sub booleans present. This works on first run, the parent boolean defaults to 0 and so the initial sub boolean field is hidden. Unfortunately, then I change the value of the parent boolean, the ajax spinner triggers, but with a breakpoint set in my IDE in the callback function I never see any evidence that the callback is called. What can I do to get the change to trigger the callback.
What else I've tried
I thought maybe, since the subfield is not a required field, perhaps I could just hide it using the #states
attribute. This worked great until the moment I added another leg to the itinerary, at which point the subfield showed up on each leg, and I had to toggle the value of field_mr_home_leave to get the fields to be hidden again.
Here's the code for that:
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function missions_form_mission_request_node_form_alter(&$form, &$form_state, $form_id)
{
foreach ($form['field_mr_fc_itineraries'][LANGUAGE_NONE] as $key => &$itinerary) {
if (is_integer($key)) {
foreach ($itinerary['field_mr_fc_legs'][LANGUAGE_NONE] as $key => &$leg) {
if (is_integer($key)) {
$leg['field_mr_fc_leg_home_leave']['#states'] = array(
'visible' => array(
':input[name="field_mr_home_leave[und]"]' => array('value' => '1'),
),
);
}
}
}
}
}
I tried deleting dependencies on the same field in conditional fields, then disabled conditional fields entirely to no avail.
I've tracked this down to form_get_cache()
failing to return a cached form. I note that nothing is being cached for this form.
Newest Update
I probably need to rewrite this question to be more concise, but here is what I've found: The cache_form table is never written to. I've simplified this as far as I can. I started with a clean drupal install on Acquia Dev Desktop, I installed my module, and created a content type that just contains the field and subfield at the same level so I don't have field collections or anything else to worry about.
Here is my latest code:
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function missions_form_mission_request_node_form_alter(&$form, &$form_state, $form_id)
{
$form['field_mr_home_leave'][LANGUAGE_NONE]['#ajax'] = array(
'callback' => 'missions_hide_home_leave_toggle',
'wrapper' => 'itinerary-section',
'effect' => 'slide',
);
$form['field_mr_fc_leg_home_leave']['#prefix'] = '<div id="itinerary-section">';
$form['field_mr_fc_leg_home_leave']['#suffix'] = '</div>';
if ((!empty($form_state['values']['field_mr_home_leave']) && $form_state['values']['field_mr_home_leave'] == 1) || (empty($form_state['values']['field_mr_home_leave']) && $form['field_mr_home_leave'][LANGUAGE_NONE]['#default_value'] == 1)) {
$home_leave_toggle_access = true;
} else {
$home_leave_toggle_access = false;
}
dpm($form);
$form['field_mr_fc_leg_home_leave']['#access'] = !$home_leave_toggle_access;
$form_state['rebuild'] = TRUE;
}
function missions_hide_home_leave_toggle($form, $form_state) {
return $form['field_mr_fc_leg_home_leave'];
}
When I click the checkbox on field_mr_home_leave I see the ajax spinner go, and I see a request to /system/ajax go out. Nothing but headers come back. The log shows Invalid form POST data.
which is an error that means it couldn't find a cached form. Am I missing a step to make sure the form gets cached? Is #access the wrong thing to manipulate?
$home_leave_toggle_access
to TRUE on initial page load? Does the sub-field appear?