If you are a savvy drupal coder then you should know that it is very tricky business to create a table output with sortable header and forms in the row.

Why? is this hard to do in Drupal 6?, the answer is most example on the internet will point to do a "sortable header" in a table via theme layer, while to include a "form element" we need to utilize a form array that called from drupal_get_form() function.

To illustrate this : Most example on the internet will suggest : Create a hook_theme -> create a theme function -> use tablesort(), pager_query to fetch the sorted data -> build the rows array -> pass the header array and rows array to theme table and theme pager....

As you can see there is no room for calling drupal_get_form to correctly build our $form array.. Now in my example, instead of calling theme() function, I will build the form array first then called it via drupal_get_form then inside the $form array I will invoke the theme function... sound complex?

You can examine the code below to get a better understanding of what is going on.


First step - Create hook theme

 * Implements hook_theme().
function mymodule_theme() {
  return array(
    'mymodule_pane_table' => array(
      'arguments' => array('element' => NULL),

Second step - Build form array

 * Function to build the manage table
 * without this the submit function
 * wont work
function mymodule_form($form_state) {
  // The header array as usually found in theme table
  // data is for display, field is the sql column name, 
  // sort is the default sort direction
  // notice the send column, that is where we put the 
  // checkbox form
  $header = array(
    array('data' => t('ID'), 'field' => 'notification_id', 'sort' => 'desc'),
    array('data' => t('Order_id'), 'field' => 'order_id', 'sort' => 'desc'),
    array('data' => t('Send')),

  // Build your sql here
  $sql = "SELECT * FROM {mymodule_table}";
  // Pass the sql to the tablesort_sql here
  $sql .= tablesort_sql($header);

  // Pager related
  $limit = 10;
  $result = pager_query($sql, $limit);

  // Now build the form array
  $form = array();

  $form['mytable_group'] = array(
    '#tree' => TRUE, // Need this for easy submit processing
    '#header' => $header, // We trick Drupal to pass our $header to theme here
    '#theme' => 'mymodule_pane_table', // pass our theme function
  // Loop the database
  // as you can notice we are looping the database here instead 
  // of in the theme function
  while ($data = db_fetch_object($result)) {
    // build our checkbox here
    $form['mytable_group'][$data->notification_id]['send'] = array(
      '#type' => 'checkbox',
    // store the fetched data per row so we can
    // still process the data in theme layer
    $form['mytable_group'][$data->notification_id]['data'] = array(
      '#type' => 'value',
      '#value' => $data,

 // Submit function
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#name' => 'submit',
    '#value' => t('Process Notification'),

  return $form;

  * form submit function

function mymodule_submit(&$form, &$form_submit) {
  // do what ever needed to process the submitted data here

Final Step - Build theme function

 * Function to build notification manager table panes
 * EDIT : The function name should follow the theme function
 * You've specified in the hook_thehe above
function theme_mymodule_pane_table(&$variables) {
  // Gotcha... if you dont use '#tree => true'
  // this will break
  $element = element_children($variables);
  // looping for each element ehmm.. rows
  foreach ($element as $key) {
    // remember our stashed data?
    $data = $variables[$key]['data']['#value'];
    // Build the row
    $row = array();
    $row[] = $data->notification_id;
    $row[] = $data->order_id;
    // This is our checkbox... don't forget to use drupal_render for 
   // any form element
    $row[] = drupal_render($variables[$key]['send']);

    // register our row to collective rows
    $rows[] = $row;

  // Finally calls our theme tables, notice the stashed $variables['#header']
  // which was built in form function.
  $output .= theme('table', $variables['#header'], $rows);

   // add our pager element
  $limit = 10; // match this to the one we set in form array
  $output .= theme('pager', NULL, $limit, 1);

  return $output;


Now after looking at the codes, actually it is pretty easy huh? Give comment or fix if you spot error. Thanks