The form API by default uses the POST method. This is fine for submission forms and things that actually change things in the database, but some forms should be GET according to the HTTP protocol, such as:

search forms
views forms
other non-state changing forms

One of the (big) advantages of using GET is that the URL can be copied/pasted. Browsers expect that forms like the described SHOULD be posted using the GET-method.

Of course, I understand that some features of 'standard' form processing may not be available in GET-forms (such as file uploads), but in some cases I find that the form API doesn't work for $_GET-forms and I don't know why.

The situation is as follows. I have the following form. Notice that I commented out the first line.

The form works fine, but when I out-comment the first line, changing the method to 'get', the following doesn't work any more:
- form checking doesn't work (even if no value is selected for the department field, the form get posted anyway)
- the form_state does not contain the correct values (hence: no results are found)
- the currently set values are not shown when rebuilding the form

This has been one my main nuisances with Drupal and I don't understand why this happens. Can't imagine I'm the first one to notice this, so it must be by design. But I wonder why; GET-forms are just as useful and sometimes the 'correct' way compared to POST-forms.

I hope someone can shed some light on this.

Regards,
Bas

/**
 * Form builder callback
 */
function hap_employees_form($form, &$form_state) {
//	$form['#method'] = 'get';
	
	//Add class attributes
	$form['#attributes']['class'][] = 'medewerkers';
	
	//Department to filter on
	$form['department'] = array(
		'#required' => true,
		'#type' => 'select',
		'#title' => 'Afdeling',
		'#default_value' => '',
		'#options' => array('' => 'Filter op afdeling'),
		'#default_value' => (array_key_exists('afdeling', $_GET) ? $_GET['afdeling'] : ''),
	);
	$terms = taxonomy_get_tree(4);
	foreach ($terms as $term) {
		$form['department']['#options'][$term->tid] = $term->name; 
	}
	
	//Radio buttons for first name or last name
	$form['name_type'] = array(
		'#type' => 'radios',
		'#title' => 'Sorteer op naam',
		'#default_value' => 0,
		'#options' => array('sorteer op voornaam', 'sorteer op achternaam'),
		'#attributes' => array('class' => array('name_type')),
	);

	//First letter
	$form['first_letter'] = array(
		'#type' => 'select',
		'#title' => 'Filteren op eerste letter',
		'#default_value' => '',
		'#options' => array('', ''),
	);
	for ($i = 0; $i < 26; $i++) {
		$form['first_letter']['#options'][chr(65 + $i)] = chr(65 + $i);
	}
	
	//Submit button
	$form['submit'] = array(
		'#type' => 'submit',
		'#value' => 'Medewerkers tonen',
	);
	
	//Show current results in the form
	if (isset($form_state['storage']) && isset($form_state['storage']['results'])) {
		$form['results'] = $form_state['storage']['results'];
	}
	
	return $form;
}

/**
 * Form submit handler
 */
function hap_employees_form_submit(&$form, &$form_state) {
	//Build query
	$query = new EntityFieldQuery();
	$query->entityCondition('entity_type', 'user');
	
	$letter = $form_state['values']['first_letter'];
	if ($letter) {
		if ($form_state['values']['name_type'] == 0) {
			//Filter on first letter of first name
			$query->fieldCondition('field_voornaam', 'value', $letter . '%', 'LIKE');
		} else {
			//Filter on first letter of last name
			$query->fieldCondition('field_achternaam', 'value', $letter . '%', 'LIKE');
		}
	}
	$dept = $form_state['values']['department'];
	if ($dept) {
		$query->fieldCondition('field_afdeling', 'tid', $dept);
	}
	
	//Order by first name, or last name iff specified
	if ($letter && $form_state['values']['name_type'] != 0) {
		$query->fieldOrderBy('field_achternaam', 'value');
	} else {
		$query->fieldOrderBy('field_voornaam', 'value');
	}
	
	//Filter on post
	$query->propertyOrderBy('uid');	//Make sure that the base table is included
	$query->addTag('filter_current_post');
	
	$entities = $query->execute();
	
	//Get user results
	if (isset($entities['user'])) {
		$users = array_keys($entities['user']);
	} else {
		$users = array();
	}
	
	//Filter out users that are not of the role 'medewerker'
	$accounts = array();
	foreach ($users as $uid) {
		$account = user_load($uid);
		if ($account && in_array('medewerker', $account->roles)) {
			$accounts[] = $account;
		}
	}
		
	if (count($accounts) > 0) {
		//Create a ol for the users
		$results = array(
			'#theme' => 'item_list',
			'#items' => array(),
			'#attributes' => array('id' => 'users_results', 'class' => array('toggled')),
			'#type' => 'ol',
		);
		
		foreach($accounts as $account) {
			$naam = hap_get_user_name($account);
			$results['#items'][] .= l($naam, 'user/' . $account->uid);
		}
	} else {
		$results = array('#markup' => '<p>Geen medewerkers gevonden.</p>');
	}
	
	//Add results to form
	$form_state['storage']['results'] = $results;

	$form_state['rebuild'] = TRUE;
}

Comments

damien tournoud’s picture

Category: bug » support

The Form API only processes POST forms by default. You can force the processing by calling drupal_build_form() directly, and passing it 'method' => 'get'. If you do so, you will probably have to disable the token-based CSRF protection (add a $form['#token'] = FALSE; in your form callback); so use at your own risk.

Also note that GET requests for anonymous users are cached by default, so make sure to skip the cache during processing if the result can vary.

bvanmeurs’s picture

Category: support » bug

One thing I like to add to this: the standard pager is using GET-parameters. Is it possible to add a pager to a form like the one above in the current Drupal system?

bvanmeurs’s picture

Category: bug » support

Thanks Damien, I'll try that.

bvanmeurs’s picture

This solved it. I changed the menu callback from drupal_get_form to hap_employees_page. Everything works as it should now!

Thanks again Damien Tournoud!

function hap_employees_page() {
	$form_state = array(
		'method' => 'get',
	);
	
	$form = drupal_build_form('hap_employees_form', $form_state);
	unset($form['form_build_id']);

	return $form;
}

/**
 * Form builder callback
 */
function hap_employees_form($form, &$form_state) {
	$form['#method'] = 'get';
	$form['#token'] = FALSE;

....
}
bvanmeurs’s picture

Status: Active » Fixed

.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.