Drupal 7.x and 8.x
Turning forms into a table of information, such as can be seen on the users administration page (admin/people) or the content administration page (admin/content) is a much easier process in Drupal 7 than it was in Drupal 6. In Drupal 6 it required some tricky logic, combined with a theme function. Drupal 7 (the same in Drupal 8 as of this writing) removes the need for most of that.
Step 1: Get the Data
The first thing we need to do is get the data we will use in our table. This will be entirely dependent on what data it is that you want to show. The table I showed above is displaying user info from the database. There are various ways of getting this data, including a direct database call (though Drupal doesn't actually have first and last name columns). But in this case, I am just going to create an array with the example data. In a real situation, you would have to build this array from your database call or however else you are getting your data. Here is the example data that will be used for this tutorial:
$users = array(
array('uid' => 1, 'first_name' => 'Indy', 'last_name' => 'Jones'),
array('uid' => 2, 'first_name' => 'Darth', 'last_name' => 'Vader'),
array('uid' => 3, 'first_name' => 'Super', 'last_name' => 'Man'),
);
As you can see, there are three rows. Each row is one user's data, that contains their UID, first name and last name. This simulates a database call that would grab this data for selected users.
Step 2: Build the Header
The next thing we need to do is put together an associative (keyed) array defining the header of the table. The keys here are important as we will be using later on in the tutorial. The table we are building in this tutorial has three cells in the header; one for the checkbox column, one for first name, and one for last name. However, we can ignore the cell for the checkbox column, as Drupal will take care of this for us later. As such, we can build our header as follows:
$header = array(
'first_name' => t('First Name'),
'last_name' => t('Last Name'),
);
Step 3: Build the Data
Next, we need to build the array that will contain the data of the table. Each element in the array will correspond to one row in the HTML table we are creating. Each element of the array will be given a unique ID. This will be the value of the checkbox/radios when the form is submitted (if selected). In this case, we want to get the UID of the user, so each row will be keyed with the UID of the user. We then will key the cells of the table with the keys as we used for the keys of the header. Again, we can ignore the checkboxes/radios as Drupal will take care of this for us later.
// Initialize an empty array
$options = array();
// Next, loop through the $users array
foreach ($users as $user) {
// each element of the array is keyed with the UID
$options[$user['uid']] = array(
// 'first_name' was the key used in the header
'first_name' => $user['first_name'],
// 'last_Name' was the key used in the header
'last_name' => $user['last_name'],
);
}
If we were to analyze the $options array, we would see it looks like this:
(
[1] => Array(
[first_name] => Indy
[last_name] => Jones
)
[2] => Array(
[first_name] => Darth
[last_name] => Vader
)
[3] => Array(
[first_name] => Super
[last_name] => Man
)
)
Each element is keyed by the UID, and each element has two child elements, one for the first name and one for the last name.
Step 4: The Magic
So now we have built a header ($header), and the rows of the table ($options). All that is left is to bring it all together. Drupal 7 has a nice little theme function that we will use for this, theme_tableselect(). theme_tableselect() takes the data, turns it into a table, adds a checkbox to each row, and adds a 'select all' checkbox to the header. Handy! So lets look at how to tie this all together:
(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
);
That's it. Really. This will do all the magic behind the scenes. We don't need to do use hook_theme, and we don't need to write any theme functions. This simple render element will take care of it all for us behind the scenes.
Additional Options
The above example will render the table with checkboxes. If we want to render the table with radios, we set #multiple to FALSE:
$form['table'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#multiple' => FALSE,
);
If for some reason, you don't want the select all checkbox added to the header, you can set #js_select to FALSE:
$form['table'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#js_select' => FALSE,
);
Retrieving the Selected Element
As the form element was set as $form['table'], in the submit function, the selected value (or values) can be retrieved at $form_state['values']['table']. Any unchecked items will be given a value of 0, checked items will be given a value of the item key.
We can use the array_filter function to give us only the selected items as in the example submit handler below, which will output the selected items as a Drupal message.
function my_form_submit($form , $form_state) {
$results = array_filter($form_state['values']['table']);
drupal_set_message(print_r($results , 1));
}
Summing it up Entire Code
Here an example form definition using the code described in this tutorial
function my_form($form, $form_state) {
$users = array(
array('uid' => 1, 'first_name' => 'Indy', 'last_name' => 'Jones'),
array('uid' => 2, 'first_name' => 'Darth', 'last_name' => 'Vader'),
array('uid' => 3, 'first_name' => 'Super', 'last_name' => 'Man'),
);
$header = array(
'first_name' => t('First Name'),
'last_name' => t('Last Name'),
);
$options = array();
foreach ($users as $user) {
$options[$user['uid']] = array(
'first_name' => $user['first_name'],
'last_name' => $user['last_name'],
);
}
$form['table'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#empty' => t('No users found'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
Drupal 6.x
Occasionally you might need to display results to your users/visitors in a table and give them the option of saving the information displayed or performing some additional bulk action on that information (like on the /admin/user/user page or admin/content/node page). When views just can’t do it you may have to program the results. Here's an example of how you would do that.
This assumes you have already created a module and have results being displayed in a table or at least know how.
Step 1: Register a Theme function
If you haven’t used theme functions outside of following instructions to add code to template.php, don’t worry, adding and using a theme function is easier than you think:
/**
* Implementation of hook_theme().
*/
function my_module_theme() {
return array(
'my_theme_function' => array(
'arguments' => array(),
));
}
Note, there are a couple of options for naming your theme function. It can be named after the form function you intend to theme, or you can give it any name you want and then add it to the form using $form['#theme']
array. I've found using the name of the form function the easiest.
Step 2: Change your table row output to form fields:
Old example code:
//Example function that gets user info from database
function my_display_function() {
//db query
$sql = "SELECT * from users WHERE users.uid > 0";
//set limit for pager
$limit = 25;
//define table header
$header = array(
array('data' => t('User ID'), 'field' => 'uid', 'sort' => 'asc'),
array('data' => t('Username'), 'field' => 'name'),
array('data' => t('Status'), 'field' => 'status'),
);
//alows sorting
$tablesort = tablesort_sql($header);
//adds a pager to results - show 25 per page
$result = pager_query($sql.$tablesort, $limit);
$rows = array();
while($item = db_fetch_object($result)) {
$rows[] = array($item->uid, l($item->name, 'user/'.$item->uid), $item->status);
}
$output .= theme('table', $header, $rows);
$output .= theme('pager', NULL, $limit, 0);
return $output;
}
New Code:
Note, before adding new code, be sure to change all references to this function to drupal_get_form('my_display_function')
as it now displays a form. If it was defined using the menu hook, be sure to change "page callback" to drupal_get_form and "page arguments" to this function name.
//Example function that gets user info from database
function my_display_function() {
//db query
$sql = "SELECT * from users WHERE users.uid > 0";
//set limit for pager
$limit = 25;
//define table header
$header = array(
'', //note empty value, will use this later
array('data' => t('User ID'), 'field' => 'uid', 'sort' => 'asc'),
array('data' => t('Username'), 'field' => 'name'),
array('data' => t('Status'), 'field' => 'status'),
);
//alows sorting
$tablesort = tablesort_sql($header);
//adds a pager to results - show 25 per page
$result = pager_query($sql.$tablesort, $limit);
$form = array();
while($item = db_fetch_object($result)) {
/*Add each user id to my checkboxes array.
Only keys, no values */
$checkboxes[$item->uid] = '';
// You need unique keys for each user, so I use user id
$form['uid'][$item->uid] = array(
'#value' => $item->uid
);
$form['name'][$item->uid] = array(
'#value' => l($item->name, 'user/'.$item->uid)
);
//
$form['status'][$item->uid] = array(
'#value' => $item->status
);
}
$form['checkboxes'] = array('#type' => 'checkboxes', '#options' => $checkboxes);
$form['pager'] = array('#value' => theme('pager', NULL, $limit, 0));
return $form;
}
Step 3: Theme the form
/*
* Theme form to display as a table
*/
function theme_my_theme_function($form) {
//define table header
$header = array(
theme('table_select_header_cell'), //using that previously empty field
array('data' => t('User ID'), 'field' => 'uid', 'sort' => 'asc'),
array('data' => t('Username'), 'field' => 'name'),
array('data' => t('Status'), 'field' => 'status'),
);
if(!empty($form['checkboxes']['#options'])) {
foreach (element_children($form['uid']) as $key) {
$rows[] = array(
drupal_render($form['checkboxes'][$key]),
drupal_render($form['uid'][$key]),
drupal_render($form['name'][$key]),
drupal_render($form['status'][$key]),
);
}
}
else {
$rows[] = array(array('data' => '<div class="error">No users found</div>', 'colspan' => 4));
}
$output .= theme('table', $header, $rows);
if ($form['pager']['#value']) {
$output .= drupal_render($form['pager']);
}
$output .= drupal_render($form);
return $output;
}
And that should do it.
Key points to note: Whatever your theme function is defined as in your implementation of hook_theme, the actual function should start with theme_(then the function you defined)
Comments
Empty resultset means empty options. Not empty form.
In function theme_my_theme_function() I think that the
if(!empty($form)) {...}
statement is wrong. The reason is that $form will never be empty. Even if we assumed that only the result of the example code was stored in the $form variable, $form is not empty, as it contains$form['checkboxes'] = array()
.Use this instead:
You're right, as I was
You're right, as I was looking at your code I was thinking "but that's what I have" (or something to that effect) and then I looked at the code I added and realized that wasn't what I had. I noticed that the first time I implemented it and I got no results but also got no message saying I got no results but only an empty table.
Thanks for noticing.
Those meddling with D7 will love this!
added comment as a revision rather.
Small note if implemented with a hook_menu
The 'page arguments' must be passed with an array:
tableselect and #default_value
I have yet to find it documented, so here goes: The #default_values attribute works differently for tableselect than for other #types. The attribute can be used to preselect options and one would assume (or at least I did) that
would preselect Darth Vader in the above example. It does not. This does:
The issue #831966: Unable to uncheck rows in default_value. Tableselect needs value callback. notes it, but it will not change i D7.
Thanks for the heads-up...
Thanks for the heads-up... here is my implementation to make it work:
Rendering Radio Buttons
When radio buttons are required setting #multiple = FALSE will render them. The documentation says TRUE causes them to be rendered.
The code shown output "no user found"
The code shown in the example above always output "no user found"
Almost same for D7 version: "No content available."
Using the code above show me a table with the headers and just "No content available." below that. Using dpm() on $form does show the #options.
Put the example code into custom block (D7, 7-28)
@ Zahor mexicoder, ronan.orb, drupalshrek, nohup. Thanks a lot!
I have put the example code above into the sandbox.
Even though I followed the guide, I have spent some hard time making it work.
I hope it saves someone some time.
or here
It is worth noting that the
It is worth noting that the
It took me a while to find what is wrong with the 0 key.
Hello ,
Hello ,
In Drupal 6, I am using tableselect theme with PagerSelect to generate table with checkboxes and pagination. Any thoughts on how to retain checkbox values on pagination?
Because in the first page , i checked 2 checkbox and go to the second page and check other checkbox
When i go back to the first page , the checked checkbox are lost
How can i fix it ?
where to put all the codes,
where to put all the codes, im new in drupal. please help.
Why here '#title' option is
Why here '#title' option is not available, like in regular table?
Validation for tableselect
It took me a while to find this, but I think it should live here as well. I was trying to validate the tableselect form and https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21... is the best place to see how to do this.
Field type "value" doesn't work anymore above 9.2.13 version
In release https://www.drupal.org/project/drupal/releases/9.2.13 security patch added to form builder. after that field type "value" doesn't return any values in form submit function.
Not sure what changes needs to be done to the above form, if any one knows please add in comments.
For more details please check below links
Release page - https://www.drupal.org/project/drupal/releases/9.2.13
security page - https://www.drupal.org/sa-core-2022-003
changes can be viewed here Changes can be viewd here - https://github.com/drupal/drupal/commit/7401e2ade3602125b244b6cbe082d555....
Below Form which used to work , but after 9.2.13 doesn't work.
Hi All,
Hi All,
I was having the same problem, after debugging form_state etc I finally found a solution that we need to use $form_state->getUserInput() to get the textfield value.
Thanks,
Sadashiv