Reputation: 51
Using the D7 form API, I need to create a hierarchical set of filters, each having a dependency of the previous filter(s). When a selection is made on one of the filters, all filters down the line are automatically updated based on the selection(s) up the chain. Seems straight forward enough, but there is a little bit more than that.
For a example, lets say I have a database with +75k product records I want to display.
Obviously I can't show all options in each select-multi, so here is how I envisioned it working:
I've been looking at the D7 form api, and I can't find any mention of this sort of functionality. I know there are #ajax and #state callbacks, but the text input auto-complete populating the select-multi and triggering events down the chain is a little fuzzy.
If it helps, I've built this entire functionality already using jQuery. My goal now is to port it over into Drupal, using the proper form API.
Upvotes: 1
Views: 1160
Reputation: 21
I know it has been long time, but would still share for the sake of others looking for a solution. You will have to implement your own flavour of each autocomplete function as per the need. The important thing to note being the autocomplete_path that is setup by using values from previous filter. Rest should be self explanatory.
// hook_menu would look something like this
function hook_menu() {
$menu = array();
$menu['hierarchical_filter'] = array(
'title' => 'Autocomplete Ajax Cascading Form',
'page callback' => 'drupal_get_form',
'page arguments' => array('hierarchical_filter_form'),
'access callback' => TRUE,
);
$menu['country_list'] = array(
'title' => 'Country List autocomplete',
'page callback' => 'country_list_autocomplete',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$menu['state_list'] = array(
'title' => 'State List autocomplete',
'page callback' => 'state_list_autocomplete',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
$menu['city_list'] = array(
'title' => 'City List autocomplete',
'page callback' => 'city_list_autocomplete',
'access callback' => TRUE,
'type' => MENU_CALLBACK,
);
return $menu;
}
function country_list_autocomplete($string='') {
$values = array('United States' => 'United States', 'South Africa' => 'South Africa', 'Russian Federation' => 'Russian Federation', 'Singapore' => 'Singapore', 'China' => 'China');
$matches = array_filter($values, function($item) use($string) {
if(stripos($item, $string) !== FALSE) return TRUE;
return FALSE;
});
drupal_json_output($matches);
}
function state_list_autocomplete($country_code, $state='') {
$values = array('South Africa' => array('Mpumalanga' => 'Mpumalanga', 'Gauteng' => 'Gauteng', 'Limpopo' => 'Limpopo', 'Northern Cape' => 'Northern Cape'),
'United States' => array('Alabama'=>'Alabama', 'Arizona'=>'Arizona'));
$matches = array_filter($values[$country_code], function($item) use($state) {
if(stripos($item, $state) !== FALSE) return TRUE;
return FALSE;
});
drupal_json_output($matches);
}
function city_list_autocomplete($state, $city='') {
$values = array('northern cape' => array('Barkly West' => 'Barkly West', 'Campbell' => 'Campbell', 'Delportshoop' => 'Delportshoop'),
'Alabama' => array('Butler'=>'Butler', 'Calera'=>'Calera', 'Helena'=>'Helena'));
$matches = array_filter($values[$state], function($item) use($city) {
if(stripos($item, $city) !== FALSE) return TRUE;
return FALSE;
});
drupal_json_output($matches);
}
function hierarchical_filter_form($form, &$form_state) {
$form = array();
$form['country_list'] = array(
'#type' => 'textfield',
'#title' => 'Choose Country',
'#autocomplete_path' => 'country_list',
'#ajax' => array(
'callback' => 'country_callback',
'wrapper' => 'states_wrapper',
),
);
$form['state_list'] = array(
'#type' => 'textfield',
'#title' => 'Choose State',
'#prefix' => '<div id="states_wrapper">',
'#suffix' => '</div>',
);
if(isset($form_state['values']['country_list'])) {
$form['state_list']['#autocomplete_path'] = 'state_list/'.$form_state['values']['country_list'];
$form['state_list']['#ajax'] = array(
'callback' => 'state_callback',
'wrapper' => 'city_wrapper',
);
}
$form['city_list'] = array(
'#type' => 'textfield',
'#title' => 'Choose City',
'#prefix' => '<div id="city_wrapper">',
'#suffix' => '</div>',
);
if(isset($form_state['values']['state_list'])) {
$form['city_list']['#autocomplete_path'] = 'city_list/'.$form_state['values']['state_list'];
}
return $form;
}
function country_callback($form, &$form_state) {
$commands = array();
// On changing country, make sure state and city fields are reset
$form['state_list']['#value'] = '';
$form['city_list']['#value'] = '';
$commands[] = ajax_command_replace('#states_wrapper', drupal_render($form['state_list']));
$commands[] = ajax_command_replace('#city_wrapper', drupal_render($form['city_list']));
return array('#type' => 'ajax', '#commands' => $commands);
}
function state_callback($form, &$form_state) {
// On changing state, make sure city field is reset
$form['city_list']['#value'] = '';
return $form['city_list'];
}
Hope this will be helpful to someone.
Upvotes: 2
Reputation: 788
Create a forum function lets say my_module_filters_form than add a menu callback using hook_menu
function my_module_menu() {
$items['my_module/ccallback' = array(
// missing a menu item type (forgot the syntax)
'callback function' => 'drupal_get_form',
'callbsck arguments' => array('my_module_filters_form'),
'access callback' => TRUE
);
}
each time a change is made in the HTML form do an ajax call to that callback and replace the HTML with your output.
Upvotes: 0