Last active
June 14, 2018 15:12
-
-
Save yann-yinn/2850342 to your computer and use it in GitHub Desktop.
Creat multiple field (add more buttons) for form api elements
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Re-usable stuff. | |
* | |
* @FIXME multifield function seems not to work in included several times in the same page. | |
*/ | |
/** | |
* Implements hook_theme() | |
*/ | |
function palpix_toolbox_theme($existing, $type, $theme, $path) { | |
return array( | |
// to add drag and drop on our "multifield". | |
// @see palpix_toolbox_fapi_multifield() | |
'palpix_toolbox_fapi_multifield_dragandrop' => array( | |
'render element' => 'element', | |
), | |
); | |
} | |
/*================================ | |
PALPIX MULTIFIED | |
generate a multiple values form api element | |
================================*/ | |
/** | |
* Transform a form api element (like a textfield) to a "multified", with optionnaly | |
* "add more" and "remove one" button like field api does with fields with unlimited values. | |
* | |
* IMPORTANT ! please note that you MUST put $form and $form state in signature of your form function declaration | |
* or it *won't work*, as those variables are required when rebuilding form in ajax callback. ! example : | |
* | |
* @code | |
* function my_form($form, $form_state) { | |
* // define your form here | |
* } | |
* @endcode | |
* | |
* Example code to transform a simple textfield to a multiple textfield : | |
* | |
* @code | |
* $form['header']['myfield'] = array( | |
* '#type' => 'textfield', | |
* '#title' => 'Kant', | |
* '#autocomplete_path' => 'user/autocomplete', | |
* '#description' => "Add some users.", | |
* // from here, properties are specific to our function palpix_toolbox_multifield | |
* '#number' => 4, // how many fields we want to display by default. | |
* '#add_more' => TRUE, // set to false if you don't want "add more" button | |
* '#collapsible' => TRUE, // for the automatically generated fieldset | |
* '#collapsed' => FALSE, // idem | |
* '#default_values' => array('test', 'deux'), // default value, one entry per field. | |
* ); | |
* | |
* // magic happen here : | |
* palpix_toolbox_multifield(array('header', 'myfield'), $form, $form_state); | |
* @endcode | |
* | |
* @parents | |
* position of field. @see drupal_array_get_nested_value() comments for more details. | |
* @param $form | |
* full form $form array | |
* @param $form_state | |
* full form $form_state | |
*/ | |
function palpix_toolbox_multifield($parents, &$form, &$form_state) { | |
// get the field we want to transform to a multifield. | |
$field = drupal_array_get_nested_value($form, $parents); | |
// first parent is the key of our field array. | |
$element_name = end($parents); | |
// create a id for the fieldset. Will be used as a css id and a form api for fieldset. | |
$fieldset_id = "fieldset_multifield_$element_name"; | |
// keep in form_state parents for this element, so that we know which field to replace in html / ajax | |
// We use $element_name as a key in array to store information per field (this is necessary if there is | |
// several instance of multifield in the same page.). In submit function, we'll be able to retrieve | |
// information looking at $form_state['triggering_element'] which contains $element_name too. | |
$form_state['palpix_fapi_multifield'][$element_name]['parents'] = $parents; | |
// create a fieldset to put our field in. | |
$form_chunk[$fieldset_id] = array( | |
'#type' => 'fieldset', | |
'#prefix' => '<div id="' . $fieldset_id . '">', | |
'#suffix' => '</div>', | |
); | |
// move some properties of our field to our fieldset : $property_name => $default_value. | |
$additional_properties = array( | |
'#title' => '', | |
'#description' => '', | |
'#collapsible' => TRUE, | |
'#collapsed' => FALSE | |
); | |
foreach ($additional_properties as $property => $default_value) { | |
// if property is defined, move it from field to fieldset | |
if (isset($field[$property])) { | |
$form_chunk[$fieldset_id][$property] = $field[$property]; | |
unset($field[$property]); | |
} | |
// if not, set default value for this property. | |
else { | |
$form_chunk[$fieldset_id][$property] = $default_value; | |
} | |
} | |
// 'fields_number' contains the number of fields to display to the user for this field instance. | |
// If empty, we display only number of field specified in $field[#number]; except if previous saved values are superior to default number of fields. | |
if (empty($form_state['palpix_fapi_multifield'][$element_name]['fields_number'])) { | |
$fields_number = count($field['#default_values']) > $field['#number'] ? count($field['#default_values']) : $field['#number']; | |
$form_state['palpix_fapi_multifield'][$element_name]['fields_number'] = $fields_number; | |
} | |
// now insert our $field inside the $fieldset. We print as many fields as needed. | |
// 'fields_number" is incremented or decremented when clicking on "add more" or "remove one" button. | |
$field_name = "multifield_$element_name"; | |
for ($i = 0; $i < $form_state['palpix_fapi_multifield'][$element_name]['fields_number']; $i++) { | |
$form_chunk[$fieldset_id][$field_name][$i]['value'] = $field; | |
$form_chunk[$fieldset_id][$field_name][$i]['value']['#default_value'] = isset($field['#default_values'][$i]) ? $field['#default_values'][$i] : ''; | |
// we need to add weight for drag and drop. | |
$form_chunk[$fieldset_id][$field_name][$i]['weight'] = array( | |
'#type' => 'weight', | |
'#title' => t('Weight'), | |
'#default_value' => $i, | |
'#title-display' => 'invisible', | |
// a class is required by drag and drop. | |
'#attributes' => array('class' => array('palpix-multifield-item-weight')), | |
); | |
} | |
// set tree to TRUE allow us to put all variables of our field in an handy array in form_state['values']. | |
$form_chunk[$fieldset_id][$field_name]['#tree'] = TRUE; | |
// this theme function add drag and drop goodness to our field. | |
$form_chunk[$fieldset_id][$field_name]['#theme'] = 'palpix_toolbox_fapi_multifield_dragandrop'; | |
// add all ajax stuff to add / remove dynamically fields. | |
if ($field['#add_more']) { | |
// add more button | |
$form_chunk[$fieldset_id]["palpix_fapi_multifield_add_more"] = array( | |
// #element_name is a fake property, so that we easily retrieve element_name in submit callback. | |
'#element_name' => $element_name, | |
'#type' => 'submit', | |
'#value' => t('Add one more'), | |
'#name' =>" palpix_fapi_multifield_add_more_$element_name", | |
'#submit' => array("palpix_fapi_multifield_submit_add_more"), | |
// this property avoid title complaining about being empty whan adding more values | |
'#limit_validation_errors' => array(array($fieldset_id, $element_name)), | |
'#ajax' => array( | |
'callback' => 'palpix_fapi_multifield_ajax', | |
'wrapper' => $fieldset_id, | |
), | |
); | |
// remove one button | |
if ($form_state['palpix_fapi_multifield'][$element_name]['fields_number'] > 1) { | |
$form_chunk[$fieldset_id]["palpix_fapi_multifield_remove_one"] = array( | |
// #element_name is a fake property, so that we easily retrieve element_name in submit callback. | |
'#element_name' => $element_name, | |
'#type' => 'submit', | |
'#value' => t('Remove one'), | |
'#name' =>" palpix_fapi_multifield_remove_one_$element_name", | |
'#submit' => array('palpix_fapi_multifield_submit_remove_one'), | |
// only validate datas from our fieldset, our all the form will be validated when clicking "add more". | |
// This cause fields to complains because they're still blanck at this moment. | |
'#limit_validation_errors' => array(array($fieldset_id, $element_name)), | |
'#ajax' => array( | |
//'path' => 'palpix-multifield-ajax-callback', | |
'callback' => 'palpix_fapi_multifield_ajax', | |
'wrapper' => $fieldset_id, | |
), | |
); | |
} | |
} | |
// add our fieldset to form. Happy end. | |
drupal_array_set_nested_value($form, $parents, $form_chunk); | |
} | |
/** | |
* Submit handler for the "add-one-more" button. | |
* | |
* Increments the max counter and causes a rebuild. | |
*/ | |
function palpix_fapi_multifield_submit_add_more($form, &$form_state, $arg) { | |
// we need to retrieve $element_name to get the right value from $form_state value. | |
$element_name = $form_state['triggering_element']['#element_name']; | |
$form_state['palpix_fapi_multifield'][$element_name]['fields_number']++; | |
$form_state['rebuild'] = TRUE; | |
} | |
/** | |
* Submit handler for the "remove one" button. | |
* | |
* Decrements the max counter and causes a form rebuild. | |
*/ | |
function palpix_fapi_multifield_submit_remove_one($form, &$form_state) { | |
$element_name = $form_state['triggering_element']['#element_name']; | |
if ($form_state['palpix_fapi_multifield'][$element_name]['fields_number']) { | |
$form_state['palpix_fapi_multifield'][$element_name]['fields_number']--; | |
} | |
$form_state['rebuild'] = TRUE; | |
} | |
/** | |
* Callback for both ajax-enabled buttons. | |
* | |
* Return portion of the form to rebuild, with added or removed fields. | |
*/ | |
function palpix_fapi_multifield_ajax($form, $form_state) { | |
// we need to retrieve $element_name to get the right value from $form_state value. | |
$element_name = $form_state['triggering_element']['#element_name']; | |
$parents = $form_state['palpix_fapi_multifield'][$element_name]['parents']; | |
$output = drupal_array_get_nested_value($form, $parents); | |
return $output; | |
} | |
/** | |
* Theme for make our multifield draggable. | |
*/ | |
function theme_palpix_toolbox_fapi_multifield_dragandrop($variables) { | |
$element = $variables['element']; | |
$output = ''; | |
$rows = array(); | |
foreach (element_children($element) as $id) { | |
$rows[$id]['data'][] = drupal_render($element[$id]['value']); | |
$rows[$id]['data'][] = drupal_render($element[$id]['weight']); | |
$rows[$id]['class'][] = 'draggable'; | |
} | |
// We now define the table header values. Ensure that the 'header' count | |
// matches the final column count for your table. | |
$header = array(t('title'), t('Weight')); | |
// set a uniq id for this table. | |
$table_id = 'palpix-multifield-table_' . $element['#id']; | |
// We can now render our tabledrag table for output. | |
$output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $table_id))); | |
// just in case... | |
$output .= drupal_render_children($element); | |
// include js. | |
drupal_add_tabledrag($table_id, 'order', 'sibling', 'palpix-multifield-item-weight'); | |
return $output; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment