Create Drupal form using theme_table() like module list form

I am showing here an example for Drupal form like you saw in Module list pages.

Drupal Form using theme_table() function

Drupal Form using theme_table() function

I am considering here an example of Featured Product Management form so we can easily understand it.

Step 1:- Create a menu hook for registering your page path. This registered path will display the featured product management page.

function product_menu() {
	$items = array();
	$items['featured_product_mgmt'] = array(
		‘title’ => ‘Manage featured product’,
		‘page callback’ => ‘drupal_get_form’,
		‘page arguments’ => array(’featured_product_form’),
		‘access arguments’ => array(’access product’),
		‘type’ => MENU_CALLBACK,
    return $items;

Step 2:- Create form object for your form. Here the important part is “featured” form element of type “checkboxes”. Checkboxes type is a group of checkbox and format a set of checkboxes. #options is an associative array, where the key is the #return_value of the checkbox and the value is displayed. We will store other information like name, category, discount etc. of product in form array so we can display it later.

function featured_product_form() {
	$query = “SELECT p.*, UNIX_TIMESTAMP(p.create_date) AS create_date, FROM {product} p LEFT JOIN {category} c ON p.cid = c.cid
	WHERE p.status = 1 ORDER BY p.product_name”;
	$rs = db_query($query);
	$featured_products = featured_product();
	$status = array();
	if ($rs) {
		while ($data = db_fetch_object($rs)) {
            $options[$data->productid] =;
            $form[$data->productid]['name'] = array(#value’ => stripslashes($data->product_name));
            $form[$data->productid]['category'] = array(#value’ => stripslashes($data->name));
            $form[$data->productid]['discount'] = array(#value’ => $data->discount . ‘%’);
            $form[$data->productid]['createdon'] = array(#value’ => date(’m-d-Y’, $data->create_date));
            if (in_array($data->productid, $featured_products)) {
                $status[] = $data->productid;
	$form['featured'] = array(#type’ => ‘checkboxes’,
#options’ => $options,
#default_value’ => $status,
	$form['submit'] = array(#type’ => ’submit’,
#value’ => t(’Submit’),
	$form['cancel'] = array(#type’ => ‘markup’,
#value’ => l(t(’Cancel’), ‘dashboard’),
	$form['#redirect'] = ‘featured_product_mgmt’;
	return $form;

Step 3:- Create a form submit function. Any form submitted using the “submit” button will pass to their corresponding function if it is available. We will extract the user input from the form array and update our database accordingly.

function featured_product_form_submit($form_id, $form) {
	$form_values = $form['values'];
	$featured = $form_values['featured'];
	$selected_products = array();
	foreach($featured as $key => $value) {
		if ($value) {
			$selected_products[] =($value’);
	$value_string = @implode(,', $selected_products);
	// Delete all previous featured products
	$query = “DELETE FROM {product_featured}”;
	// Insert new featured products
	if (count($selected_products)) {
		$query = “INSERT INTO {product_featured}(productid) VALUES $value_string”;
	drupal_set_message(t(’Featured product list has been updated successfully.’));

Step 4:- Register your modules theme implementation using “hook_theme()” function.

function product_theme() {
	return array(
		‘featured_product_form’ => array(‘arguments’ => array(’form’ => NULL),),

Step 5:- Then finally define your theme function which actually format your form layout in list using the $form array. Using “foreach” loop we navigate through each item of $form array and create $rows array which we pass to “theme_table()” function later. Create the header of your table and call “table_theme()” function with $header and $rows parameters. Call the “drupal_render()” function for render the submit and cancel buttons.

function theme_featured_product_form($form) {
	$rows = array();
	foreach (element_children($form) as $key) {
		$row = array();
		if (isset($form[$key]['name'])) {
			$status = drupal_render($form['featured'][$key]);
			$row[] = array(’data’ => $status,class=> ‘checkbox’);
			$row[] = ‘‘. drupal_render($form[$key]['name']) .’‘;
			$row[] = array(’data’ => drupal_render($form[$key]['category']));
			$row[] = array(’data’ => drupal_render($form[$key]['discount']));
			$row[] = array(’data’ => drupal_render($form[$key]['createdon']));
			$rows[] = $row;
	// Individual table headers.
	$header = array();
	$header[] = array(’data’ => t(’Featured’),class=> ‘checkbox’);
	$header[] = t(’Name’);
	$header[] = t(’Category’);
	$header[] = t(’Discount’);
	$header[] = t(’Created on’);
	$output = theme(’table’, $header, $rows);
	$output .= drupal_render($form);
	return $output;

, ,

  1. #1 by Marita - April 28th, 2009 at 22:41

    In step 2, you have this line:

    $featured_products = featured_product();

    What is this function, featured_product? I was trying to duplicate this and got stuck here!

  2. #2 by admin - April 29th, 2009 at 10:40

    Hi Marita,

    Thanks for showing interest in this post.

    “featured_product()” is a function which return an array of product ids which are set as featured.

    We need this to show previously selected featured products checked.

    Here is the code:
    function featured_product() {
    $query = “SELECT *, UNIX_TIMESTAMP(create_date) AS create_date FROM {product_featured} ORDER BY create_date DESC”;
    $rs = db_query($query);

    $featured_products = array();

    if ($rs) {
    while ($data = db_fetch_object($rs)) {
    $featured_products[] = $data->productid;

    return $featured_products;

  3. #3 by Drupal_phobia - June 22nd, 2009 at 16:42

    I got as far as theme_featured_product_form($form) function. Now what do I do? How do I test it? Where do I call this function from to get my table printed on the page? I know such a stupid question, but is killing me.

    Thanks for sharing this with us newbies…

  4. #4 by Drupal_phobia - June 22nd, 2009 at 16:49

    I got it to work. Looks great…

    Thanks again!

    Apparently I had to disable and enable the my module so the changes to the theme take effect.


  5. #5 by admin - June 22nd, 2009 at 17:13

    First create a module name “product”. Then write the code given in step 1 to 5 in this module.
    Then activate this module and after activation visit the path “”.

    You also require few things before this.
    function product_perm() {
    return array(‘access product’);
    Give the “access product” permission to the appropriate role for which you want this path accessible.

    2. You need 3 tables in your database.
    a) product
    CREATE TABLE `product` (
    `productid` int(10) unsigned NOT NULL auto_increment,
    `product_name` varchar(256) NOT NULL,
    `description` tinytext,
    `long_description` mediumtext,
    `cid` int(10) unsigned default NULL,
    `image_path` varchar(256) default NULL,
    `discount` int(10) default ‘0’,
    `create_date` timestamp NOT NULL default CURRENT_TIMESTAMP,
    `restricted_state` tinytext,
    `status` int(1) unsigned NOT NULL default ‘1’,
    PRIMARY KEY (`productid`)
    b) category
    CREATE TABLE `category` (
    `cid` int(10) unsigned NOT NULL auto_increment,
    `name` varchar(256) NOT NULL,
    `pid` int(10) NOT NULL default ‘-1′,
    `create_date` timestamp NOT NULL default CURRENT_TIMESTAMP,
    `status` int(1) unsigned NOT NULL default ‘1’,
    PRIMARY KEY (`cid`)
    c) product_featured
    CREATE TABLE `product_featured` (
    `productid` int(10) unsigned NOT NULL,
    `create_date` timestamp NOT NULL default CURRENT_TIMESTAMP,
    PRIMARY KEY (`productid`)

    3) Fill some test data in these tables.

  6. #6 by admin - June 22nd, 2009 at 17:14

  7. #7 by Drupal_phobia - June 22nd, 2009 at 18:14

    Thanks for the quick reply. I have amended the code so it queries my tables and it is working fine. However I have another issue. I Have another drop-down menu part of the same form. I am trying to add everything to one fieldset, so i can make everything collapsable for easy navigation. I am finding it hard to add your themed form to the existing fieldset.

    Sorry to bother you again. If you don’t have time for it that’s OK.


  8. #8 by Bobodeman - August 6th, 2009 at 18:17

    Great. Hate to ask to much. But would it be possible to use AHAH options to make the table update each record with Ajax?


  9. #9 by Don V - August 28th, 2009 at 08:22


    Thank you for this writeup. It pushed me through a stumbling block I was having and had about given up on.

  10. #10 by Mark - August 31st, 2009 at 18:34

    Great two part post. I’ve learned a lot by studying the code.

    How did you get the alternating row colors? Did I miss something/

  11. #11 by admin - August 31st, 2009 at 18:47

    @ Mark
    When you use theme(’table’, $header, $rows) function it add <tr class="odd"> and <tr class="even"> in rendered HTML for alternative rows. So, what you basically need to do is just define these "odd" and "even" classes in your css file. You can try something like this:
    tr.odd {
    background-color: #edf5fa;
    tr.even {
    background-color: #fff;

  12. #12 by Mark - August 31st, 2009 at 19:55

    That was pretty easy to do. Thanks.

  13. #13 by Javilete - October 13th, 2009 at 20:07

    Hi, I have been studying your code and it is quite similar to mine, but the result I dont think so because in my case It is shown all data from the data base but the checkboxes no, what do I do wrong?? I am stuck here and I can’t go on, I would appreciate any help.
    thanks in advance.

  14. #14 by Javilete - October 13th, 2009 at 21:08

    I haven’t got to work the function theme_featured_product_form($form) disabling and enabling the modulo, how do I do it??

  15. #15 by Finau - October 19th, 2009 at 05:24

    If I want to add the sort functionality to my table header how can I do that. I know the query is generate from the drupal_get_form function not inside theme_ function.

    Any ideas would be appreciated.


  16. #16 by javilete - October 22nd, 2009 at 20:22

    what do you mean exactly with “add the sort functionality”??

  17. #17 by Steven - October 27th, 2009 at 15:10

    Hello everybody

    I find this a great tutorial… but it doesn’t work for me… What am i doing wrong?

    I have a database with only one extra table: shoppingbasket


    my products comes from teh node table

    ‘page callback’ => ‘shoppingbasket_list’,
    ‘access arguments’ => array(‘access shoppingbasket’),
    ‘menu_name’ => ‘primary-links’

    return $items;

    function shoppingbasket_list() {
    global $user;
    $userId = $user->uid;

    $query = “SELECT,
    sb.state AS stateId,
    n.title AS productName
    FROM {shoppingbasket sb, node n}
    WHERE sb.userId = %d
    AND sb.productId = n.nid”;

    $rs = db_query($query, $userId);

    $status = array();

    if ($rs) {
    while ($data = db_fetch_object($rs)) {

    case 1:
    $data->state = ‘goedgekeurd';
    case 2:
    $data->state = ‘in aanvraag';
    case 3:
    $data->state = ‘afgekeurd';
    $options[$data->id] =”;
    $form[$data->id][‘product’] = array(‘#value’ => stripslashes($data->productName));
    $form[$data->id][‘state’] = array(‘#value’ => stripslashes($data->state));
    $status[] = $data->productid;

    $form[‘featured’] = array(
    ‘#type’ => ‘checkboxes’,
    ‘#options’ => $options,
    ‘#default_value’ => $status

    $form[‘submit’] = array(
    ‘#type’ => ‘submit’,
    ‘#value’ => t(‘Delete’)

    $form[‘#redirect’] = ‘featured_product_mgmt';

    return $form;

    function shoppingbasket_theme() {
    return array(
    ‘shoppingbasket_form’ => array(‘arguments’ => array(‘form’ => NULL),),

    * Styling the tabale

    function theme_shoppingbasket_form($form) {
    $rows = array();
    foreach (element_children($form) as $key) {
    $row = array();
    if (isset($form[$key][‘name’])) {

    $status = drupal_render($form[‘featured’][$key]);
    $row[] = array(‘data’ => $status, ‘class’ => ‘checkbox’);
    $row[] = array(‘data’ => drupal_render($form[$key][‘product’]));
    $row[] = array(‘data’ => drupal_render($form[$key][‘state’]));

    $rows[] = $row;

    // Individual table headers.
    $header = array();
    $header[] = array(‘data’ => t(‘Delete’), ‘class’ => ‘checkbox’);
    $header[] = t(‘Product’);
    $header[] = t(‘State’);

    $output = theme(‘table’, $header, $rows);
    $output .= drupal_render($form);
    return $output;


  18. #18 by andy - November 13th, 2009 at 03:28

    hey is there any way we can send post or get value while
    redirecting page for the following command..

    $form[‘#redirect’] = ‘featured_product_mgmt’;

  19. #19 by waller - January 24th, 2010 at 06:45

    it does not work for me!!
    there is error in this line:

    $options[$data->productid] = ”;

  20. #20 by waller - January 25th, 2010 at 07:32

    Finally it works for me, thank you

  21. #21 by berto - August 28th, 2010 at 08:08

    Hi, this is a nice tutorial. I’m building a site which needs some functionality close to this. Instead of the checkboxes, I want a select box. When I do this however, it breaks the table, and is displayed outside of it. Just changing it back to checkboxes fixes the situation. Is there a problem displaying ‘select’ boxes in tables?

  22. #22 by web design patterns - August 31st, 2010 at 03:36

    Either I do something wrong, or just can’t grasp the (REQUIRED!!) connection between the form and the table rendering. For what I came here :) Or maybe it’s a late hour’s fault….
    But looks great!

  23. #23 by Essays - September 22nd, 2010 at 16:37

    Thanks for this. I am very beginner in drupal and learning something each day. I do not have any problems with time showing in the node but still i guess i may need it as i progress in drupal for better user experience.

  24. #24 by antique cabinet - October 21st, 2010 at 00:33

    I haven’t tried Drupal yet, this is a nice start, thanks for the tutorial.

  25. #25 by Manoj - November 3rd, 2010 at 10:30

    I got the form displayed but on submitting it my submit function does not get called. Please help.

  26. #26 by Pierre Brun - December 15th, 2010 at 21:51

    I have also a problem on submit method.

    Any body have an answer ?

  27. #27 by Finance - January 5th, 2011 at 17:59

    some functionality close to this. Instead of the checkboxes, I want a select box. When I do this however, it breaks the table, and is displayed outside of it. Just changing it back to checkboxes fixes the situation. Is there a problem displaying ‘select’ boxes in tables?

  28. #28 by fawwad - February 21st, 2011 at 16:10

    it is very good post.

  29. #29 by Ben - March 23rd, 2011 at 18:05

    I’m no Drupal expert, but this should be enough to get you moving in the right direction. Field is what determines if sorting is rendered….obviously you’ll have to handle it, but this should help you get started.

    $header = array();
    $header[] = array(‘data’ => t(”), ‘class’ => ‘checkbox’);
    $header[] = array(‘data’ => t(‘Address’), field=>’address’);
    $header[] = array(‘data’ => t(‘City’), field=>’city’);
    $header[] = array(‘data’ => t(‘State’), field=>’state’);

  30. #30 by keithyau - April 6th, 2011 at 08:50

    Hi, Do anyone know how to add a filter on the table above?

    Like the views filter

    Many thanks

  31. #31 by sanjay singh chauhan - September 27th, 2011 at 12:45

    hi i m biginer of drupal and i want to be a module devloper can u plz help me Mr.Chauhan????????

Comments are closed.