Drupal

Debugging with Git Bisect

As most of you know, the marvelous git version control system is the future of the Drupal repository. You've probably heard people rave about lots of very important features: It's distributed, FAST, branching and merging are easy.

But today I'm here to rave about a more obscure wonderful feature of git: the git bisect command. With this command you can find out where in the history a particular bug was introduced. I made a short screencast to explain it:

The basic idea: The fastest way to understand a problem is to divide it in half, then divide that in half, etc. In search technology this is called binary search. In military terms it's called "divide and conquer". It's great. It's git bisect. You choose a version of your code that's broken. Then you choose a historical revision that's not broken. Then git just checks out for you the halfway points for you to test until you get to the actual answer to your question: in what commit was the change introduced?

One key feature of git that this showcases is its incredible speed. You could do all this (manually) with any revision control system. But how long would it take to do each checkout? Way too long to make it practical in most cases.

Git is cool. This is just one more reason.

AJAX Form Resources

AJAX Example: Select control and generated checkboxes

In this example we use a select control to determine how many checkboxes are generated.

(This one is live at http://d7.drupalexamples.info/examples/ajax_example/autocheckboxes and the code on api.drupal.org at http://api.drupal.org/api/function/ajax_example_autocheckboxes/7)

<?php
/**
* AJAX-enabled select element causes replacement of a set of checkboxes
* based on the selection.
*/
function ajax_example_autocheckboxes($form, &$form_state) {

 
$default = !empty($form_state['values']['howmany']) ? $form_state['values']['howmany'] : 1;

 
$form['howmany_select'] = array(
   
'#title' => t('How many checkboxes do you want?'),
   
'#type' => 'select',
   
'#options' => array(1 => 1, 2 => 2, 3 => 3, 4 => 4),
   
'#default_value' => $default,
   
'#ajax' => array(
     
'callback' => 'ajax_example_autocheckboxes_callback',
     
'wrapper' => 'checkboxes-div',
     
'method' => 'replace',
     
'effect' => 'fade',
    ),

  );


 
$form['checkboxes_fieldset'] = array(
   
'#title' => t("Generated Checkboxes"),
   
// The prefix/suffix provide the div that we're replacing, named by
    // #ajax['wrapper'] above.
   
'#prefix' => '<div id="checkboxes-div">',
   
'#suffix' => '</div>',
   
'#type' => 'fieldset',
   
'#description' => t('This is where we get automatically generated checkboxes'),
  );

 
$num_checkboxes = !empty($form_state['values']['howmany_select']) ? $form_state['values']['howmany_select'] : 1;
  for (
$i=1; $i<=$num_checkboxes; $i++) {
   
$form['checkboxes_fieldset']["checkbox$i"] = array(
     
'#type' => 'checkbox',
     
'#title' => "Checkbox $i",
    );
  }

 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Submit'),
  );

  return
$form;
}

/**
* Callback element needs only select the portion of the form to be updated.
* Since #ajax['callback'] return can be HTML or a renderable array (or an
* array of commands), we can just return a piece of the form.
*/
function ajax_example_autocheckboxes_callback($form, $form_state) {
  return
$form['checkboxes_fieldset'];
}
?>

AJAX Simplest Example

The essence of AJAX forms is that you mark an element of a form as the one that activates AJAX behavior, and you tell it what section of the HTML is to be replaced when it's activated.

The form we're using here has a textfield with a description, which is just plain HTML. It has a prefix and suffix that mention its CSS ID. The CSS ID will be used by the javascript running in the background to determine what's to be replaced.

$form['changethis'] is marked as AJAX-enabled with the "#ajax" marker, which tells the Form API its callback function and the HTML ID (wrapper) to replace.

When $form['changethis'] is changed, javascript provided by Drupal makes a request to the server, which rebuilds the form and calls the callback, which selects a piece of the form. The resulting form piece is rendered and returned to the page, and is replaced by the javascript that made the original call.

(This one is live at http://d7.drupalexamples.info/examples/ajax_example/simplest and the code is on api.drupal.org at http://api.drupal.org/api/function/ajax_example_simplest/7)

Here's an example of the very simplest AJAX behavior from the Examples module.

<?php
/**
* Simple form whose ajax-enabled 'changethis' member causes a text change
* in the description of the 'replace_textfield' member.
*/
function ajax_example_simplest($form, &$form_state) {
 
$form = array();
 
$form['changethis'] = array(
   
'#title' => t("Choose something and explain why"),
   
'#type' => 'select',
   
'#options' => array(
     
'one' => 'one',
     
'two' => 'two',
     
'three' => 'three',
    ),
   
'#ajax' => array(
     
'callback' => 'ajax_example_simplest_callback',
     
'wrapper' => 'replace_textfield_div',
     ),
  );

 
// This entire form element will be replaced with an updated value.
  // However, it has to have the prefix/suffix to work right, as the entire
  // div is replaced.
  // In this example, the description is dynamically updated during form
  // rebuild.

 
$form['replace_textfield'] = array(
   
'#type' => 'textfield',
   
'#title' => t("Why"),
   
'#prefix' => '<div id="replace_textfield_div">',
   
'#suffix' => '</div>',
  );

  if (!empty(
$form_state['values']['changethis'])) {
   
$form['replace_textfield']['#description'] = t("Say why you chose") .  " '{$form_state['values']['changethis']}'";
  }
  return
$form;
}
/**
* Callback for ajax_example_simplest.
*
* The form item 'replace_textfield' has already been processed and changed
* when the form was submitted and rebuilt during the AJAX call, so now
* we just return the piece of the form that changed.
*/
function ajax_example_simplest_callback($form, $form_state) {
 
// The form has already been submitted and updated. We can return the replaced
  // item as it is.
 
return $form['replace_textfield'];
}
?>

30 Seconds on the Drupal Form API

It's pretty easy to forget all the details of the Drupal Forms API, so we'll stop for a minute for a very short review.

(This one is at ahah_demo/simple_form.)

You add a form to your page like this:

<?php
$output
.= drupal_get_form('ahah_demo_simplest_form');
?>

Often that's done right from the hook_menu implementation.

The layout of a form is as below:

<?php
/**
* A Hello-world for Forms API (FAPI)
*/
function ajax_demo_simplest_form($form, &$form_state) {

 
$form['explanation'] = array(
   
'#type' => 'markup',
   
'#value' => '<div>' . t('This is a basic form with just a bit of information') . '</div>',
  );

 
$form['experience'] = array(
   
'#type' => 'select',
   
'#title' => t('What is your experience level in Drupal?'),
   
'#options' => array(
     
'expert' => t('Expert'),
     
'journeyman' => t('Journeyman'),
     
'beginner' => t('Beginner')),
  );
 
$form['more'] = array(
   
'#type' => 'textfield',
   
'#title' => t("Say more about yourself"),
   
'#description' => t('Surely one word can\'t describe you.'),
  );
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => t('Click Me'),
  );

  return
$form;
}


function
ajax_demo_simplest_form_submit($form, &$form_state) {
 
drupal_set_message(t('You have claimed to be %experience: %more',
    array(
     
'%experience'=>$form_state['values']['experience'],
     
'%more' => $form_state['values']['more'],
    )
  ));
}
?>

Examples of AJAX forms in Drupal

  • Book module (core) (Rebuilds the form so you can select an appropriate book and parent page)
  • Poll module (core) (Click a submit button and it adds another question)
  • Examples module (Drupal 7) provides a number of AJAX examples in the "AJAX Example module".
  • Quicktabs module (Configuration options change with the type of tab)

What are AJAX Forms in Drupal?

AJAX forms in Drupal are provided by the Form API.

  • You write the form using the Drupal Form API.
  • You specify what element activates the AJAX behavior.
  • You specify what part of the form's HTML is to be replaced by the callback.
  • You write a callback that selects and returns the piece of the form which gets replaced.
  • Drupal manages everything so that when the button or control is used, the callback gets called and the replacement of HTML is done.

Although Javascript is involved (supplied by Drupal) you don't write any and you don't see any.

Drupal 7 AJAX Forms

[This material was developed for a presentation at Drupalcamp Colorado in June of 2010. If you find any problems with it or want to suggest any improvements, send me an email or catch me on IRC (rfay).]

All of us know about "Web 2.0" and "AJAX", or "Asynchronous Javascript and XML". The basic idea behind AJAX is that a Javascript program running in the browser communicates (usually via HTTP and XML) with the webserver and gets information that it uses to do a task or update the page. Several Drupal modules use AJAX, and it's widely deployed on the web. It normally requires you to write and maintain some pretty good Javascript along with your PHP, and it's a powerful way to add interactive features to a website. Drupal's excellent contrib module Fivestar, for example, uses XML to allow you to vote without a page load.

Built into Drupal, though, is a no-visible-Javascript technique for doing AJAX form updates. It uses Javascript under the hood, but you never see it. Everything the programmer does is in PHP. And in Drupal 7 it's pretty darned easy.

Continue reading the entire presentation.

Debugging Drupal Presentation posted

My presentation for Paris Drupalcon is posted: randyfay.com/debugging_drupal.

Syndicate content