Skip to content

Instantly share code, notes, and snippets.

@swirtSJW
Created January 15, 2016 21:14
Show Gist options
  • Save swirtSJW/9f0e7ffcb69eb0c3690b to your computer and use it in GitHub Desktop.
Save swirtSJW/9f0e7ffcb69eb0c3690b to your computer and use it in GitHub Desktop.
Drupal 7 FormManipulator for adding items or moving items in an Views Exposed Filter or form
<?php
/**
* @file
* Defines FormManipulator class for modifying a Drupal 7 Form.
*/
/**
* Class with methods for manipulating a Drupal 7 form.
*
* Current methods are
* - Adding form elements to a specific location in the form.
* - Moving existing form elements to a different location.
*/
class FormManipulator {
public $form;
private $tempForm;
private $formOriginal;
/**
* Constructor.
*
* @param array $form
* Drupal form array as provided by hook_form_alter().
*/
public function __construct(&$form) {
$this->formOriginal = $form;
$this->form = $form;
$this->tempForm = array();
}
/**
* Getter for the form.
*
* @return array
* Form array.
*/
public function getForm() {
$this->updateForm();
return $this->form;
}
/**
* Setter for the form.
*/
public function setForm($new_form) {
if (!empty($new_form)) {
$this->form = $new_form;
$this->tempForm = array();
}
}
/**
* Adds a form element to the temporary form array.
*
* @param string $key
* The key to use for the added element.
* @param mixed $value
* The value of the element to be added.
*/
private function addTempElement($key, $value) {
$this->tempForm[$key] = $value;
$this->setElementInfo($key);
}
/**
* Copy the element #info into the element to survive any rearranging.
*
* @param string $key
* The form element name / key.
*/
private function setElementInfo($key) {
$info = $this->getInfo($key);
if (!empty($info)) {
$this->tempForm[$key]['#info'] = $info;
}
}
/**
* Gets any #info related to the fieldname if it exists.
*
* @param string $form_element_name
* The field element name / key from the Form array.
*
* @return array
* The #info for the element, as an array.
*/
private function getInfo($form_element_name) {
if ((!empty($this->tempForm['#info'])) && is_array($this->tempForm['#info'])) {
foreach ($this->tempForm['#info'] as $key => $elements) {
if (!empty($elements['value']) && ($elements['value'] === $form_element_name)) {
$info = array($key => $elements);
return $info;
}
}
}
}
/**
* Rebuilds the form's #info array to match the new order and field changes.
*/
private function rebuildInfo() {
if (isset($this->form['#info'])) {
// Wipe the #info array clean to start over.
$this->form['#info'] = array();
// Rebuild #info from the info stored in the form elements.
foreach ($this->form as $key => $element) {
if (!empty($element['#info'])) {
$stored_key = key($element['#info']);
$stored_value = current($element['#info']);
$this->form['#info'][$stored_key] = $stored_value;
unset($this->form[$key]['#info']);
}
}
}
}
/**
* Adds items to the appropriate location of the form array.
*
* @param array $items
* Array or arrays of form items to be added.
* @param string $location_key
* The key to use to indicate where the items should be added.
* @param bool $before
* Boolean value indicating whether the items should be inserted before or
* after the $location_key. Default(TRUE).
*/
public function addItems($items, $location_key, $before = TRUE) {
if ((is_array($items)) && is_string($location_key)) {
foreach ($this->form as $key => $current_item) {
if (($location_key === $key) && ($before === TRUE)) {
// Insert the $items first.
$this->injectItems($items);
}
// Allow moving, but not duplicating fields.
if (empty($this->tempForm[$key])) {
$this->addTempElement($key, $current_item);
}
if (($location_key === $key) && ($before !== TRUE)) {
// Insert the after the current_item.
$this->injectItems($items);
}
}
$this->setForm($this->tempForm);
}
else {
watchdog('FormManipulator', '!func Failed to insert the requested value, due to bad params.', array('!func' => 'FormManipulator->addItems'), WATCHDOG_ERROR);
}
}
/**
* Updates the form element weights and #info to the new order.
*/
public function updateForm() {
$this->setWeights();
$this->rebuildInfo();
$this->setForm($this->tempForm);
}
/**
* Set the weights to match the new order of the form elements.
*/
private function setWeights() {
// Set the weights to match the new order.
$weight = 0;
foreach ($this->form as $key => $item) {
if (!empty($item['#type'])) {
$this->form[$key]['#weight'] = $weight++;
}
}
}
/**
* Injects an array of form items into current location of $this->tempForm.
*
* @param array $items
* an array of form item arrays to inject into the form.
*/
private function injectItems($items) {
// Loop through the form items to insert.
foreach ($items as $insert_key => $item) {
if (!empty($this->tempForm[$insert_key])) {
// Remove the existing element so it will get this new location.
unset($this->tempForm[$insert_key]);
}
$this->addTempElement($insert_key, $item);
$info = $this->getInfo($insert_key);
if (empty($info)) {
// There is no #info on this type, so add some basic.
$this->tempForm[$insert_key]['#info'] = array("filter-{$insert_key}" => array('value' => $insert_key));
}
else {
// There is #info for this type, so copy it to the element temporarily.
$this->tempForm[$insert_key]['#info'] = $info;
}
}
}
/**
* Debug output the original form and the modified form with dpm.
*/
public function debugDiff() {
if (function_exists('dpm')) {
// @codingStandardsIgnoreStart
$diff = array_diff_assoc($this->form, $this->formOriginal);
dpm(t('Debug -> Original form:'));
dpm($this->formOriginal);
dpm(t('Debug -> Current form:'));
dpm($this->form);
dpm(t('Debug -> Additions to the form:'));
dpm($diff);
dpm(t('Debug -> Other changes may have been made that could not be calculated.'));
// @codingStandardsIgnoreEnd
}
}
}
<?php
require_once 'classes/FormManipulator.php';
/**
* Implements hook_form_FORM_ID_alter().
*/
function MYMODULE_form_FORM_ID_alter(&$form, &$form_state, $form_id) {
if ($form['#id'] === 'MY_FORM_ID') {
$form_man = new FormManipulator($form);
// Move submit and reset button to before the secondary filters.
$location_key = 'secondary';
$items = array(
'submit' => $form['submit'],
'reset' => $form['reset'],
);
$form_man->addItems($items, $location_key);
// Add a link to in front of the author field.
$location_key = 'author';
$items = array(
'main-shelp' => array(
'#type' => 'markup',
'#markup' => '<p><a href="/some/path/filter-help">Filter help link</a></p>'
),
);
$form_man->addItems($items, $location_key);
$form = $form_man->getForm();
// This will output some debug in dpm that will let you compare
// your changes to the form.
//$form_man->debugDiff();
}
}
@swirtSJW
Copy link
Author

Exposed Filters in Views are sometimes painful to try to add items or juggle things around beyond what can be done in CSS.
This FormManipulator class allows for this to be a pretty easy process.

Referenced here http://web-dev.wirt.us/info/drupal-7-code-snippets/moving-views-exposed-filters-form-elements-around

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment