You are here

form

The multiple magics of Drupal search

Form API is magical; core Drupal search is a twist on that magic; hooking onto that twist puts your code on yet another level of weird.

Drupal's Form API handles so much work for you that you'd be a fool not to use it as much as possible. This code snippet:

function myform_some_form($form_state) {
  $form['text'] = array(
    '#type' => 'textfield',
    '#title' => t('Your submission'),
    '#default_value' => t('Enter some text'),
    '#description' => t('Please use this field to submit some text'),
    '#required' => TRUE,
  );
  return $form;
}

creats a form with:

  • A single textfield element
  • Accessible XHTML with form labels
  • Potentially localized labels, translated into any number of languages
  • A bit of similarly localized help text below the element
  • Validation of the form submission, with the field content marked as required

That's a separate item of form functionality for each array key. And as long as you use Form API, Drupal handles validation and input sanitization for you, thus massively reducing the risk of attack by SQL injection or XSS.

Bookmarkable search URLs with POSTed search terms

But there's a catch. To encourage best practice in terms of form submission and friendly URLs, Form API defaults to HTTP POST. If site searching used Form API (which it does) then what impact would that have? Successful searches could never be bookmarked, because the URL on its own doesn't capture the POST submission.

The search module tackles this by adding an extra twist to Form API. At the end of submission processing are the following two actions:

  • Call either the function named in $form['#submit'] or $ID_submit, where $ID is typically the name of the original form creation function ("myform_some_form" above)
  • Finally, either return to the original action page of the form, or redirect to any URL specified in $form['redirect']

The search module therefore uses a function called search_form_submit to grab the POSTed search terms, and redirect the user to search/$SEARCH_TYPE/$SEARCH_TERMS. $SEARCH_TYPE is "node" for Drupal's out-of-box textual node searching, but if you install some other search module e.g. Apache Solr then it'll be e.g. "apachesolr_search" instead. Result: bookmarkable search URLs.

Writing your own module to handle searches

This has important ramifications if you're trying to piggyback off core search somehow: if, say, you're still using core search or a third-party module for the actual result-finding, but then you want a page other than core search to display the results.

If you want the main site search form to redirect to your own pages, for example, then you have to (a) add your own $form['#submit'] function to the stack and then (b) use that to change the core search's $form['redirect']:

// Implementation of hook_form_alter(), adding an extra submit callback to
// search forms identified by their existing callback
function mysearch_form_alter(&$form, $form_state, $form_id) {
  $submits = array(
    'box' => 'search_box_form_submit',
    'form' => 'search_form_submit',
  );
  if (is_array($form['#submit'])) {
    $which = array_intersect($submits, $form['#submit']);
    $which && ($form['#submit'][] = 'mysearch_form_mysubmit');
  }
}
// Submit callback, which changes the redirect using a regular-expression replace
function mysearch_form_mysubmit(&$form, $form_state) {
  $form_state['redirect'] = preg_replace('/^search\/[^\/]+/', 'search/my_special_search',
    $form_state['redirect']);
}

Now you've got all your site search forms redirecting to a bookmarkable page at search/my_special_search/$SEARCH_TERMS. All you have to do now is write a menu callback for that page: from here on in you're on your own for now.

Blog category: 

Programming shouldn't be degrading

Steve compares “graceful degradation” with “progressive enhancement.” Mostly he takes issue (rightly) with the rhetorical spin that the former applies to the idea of building a website. But I think you can compare them with each other as if they were two different types of crowbar instead: two ways of prising open the task in hand.

What I like most about progressive enhancement is that it gives me a way of tackling the age-old problem of turning a design for a client into a workable, accessible page without going completely mental. Before, if I saw a dropdown on the designs without a submit button or some such, I’d think, well, the HTML won’t have a submit button either, so I need to build the Javascript at the same time and get it all to work at once. This led me into all sorts of tangles—each one on its own easy to extricate myself from—and at the end of it I would have a form that would work or look right without every layer of technology in place.

But if you’re thinking progressively at the back of your mind all the time, you can start with an unstyled, unscripted form that works, and then use subtle styling to get exactly where you want to be. Some onloaded Javascript can give you a body.js class with which to hide unwanted submit buttons. At the end, if nothing else, the form still submits when Javascript is turns off, and it looks OK because you checked at the outset.

Progressive enhancement, ironically for the amount of work it implies, actually places the least stress (and maintenance workload) on the developer, and makes them learn a thing or two about layering and modularity at the same time. And as Steve points out, in this model there is no degradation: just the near-hotswapping of a range of technologies on top of a sturdy markup language that has been around in various forms for over a decade.

Blog category: 
Subscribe to RSS - form