Tables in Drupal: Making a perfect table

Have you ever...?

Have you ever wanted to make a table in Drupal, using theme_table? Well, what about adding extra links, like for options (Edit / Delete etc)? And how about sorting it? Pager anyone? Well this tutorial will show you how to do all that. After this you will be a Drupal Table master!

Step 1: Define your headers

What are the names of the columns?

This sounds silly, but it will come in useful later on in the process.

You can find out by looking at the corresponding table in your database

You should end up with an array of headers like this:

  1. $headers = array('data' => t([field_name]));

Of course you need to replace the text in square brackets with the name of your field from the database.

Step 2: Define your Extras

The extras are the actions you want to perform on the data, such as edit or delete the data.

The easiest and cleanest way to do this is to create a separate helper function that you can call during the main table building routine.

  1. /**
  2.  * Utility Function for building links for the table
  3.  */
  4.  function _generate_options($id){
  5.         $links = l(t('Edit'), "admin/settings/data/edit/$id").
  6.         '  '.
  7.         l(t('Remove'), "admin/settings/data/remove/$id");
  8.         return $links;
  9.  }

That's not really a lot of code, and you can see it could come in quite handy. It simply builds you a link (using the l() function) with actions based on your header choices, and adds the id of the row into your link, thus ensuring you act on the correct element.

I use the underscore at the beginning of the function name to denote this is a 'private' function; it is only used internally by this module.

This function gets called in Step 4, when you process the data for building up the actual table.

Step 3: Run your query

Now, we need to set up the query to get all the data from your desired table.

  1. $sql = "SELECT [fields] FROM {[table]}";

As you can see, this is not a very complicated query. You may think it is inefficient, and you would be right. We will let Drupal take care of that later (Step 6).

Step 4: Process the data

This step is where you actually get the data from the database into an array, ready to be themed into a table.

  1. //run query
  2. $result = db_query($sql);
  3. if($result){
  4.         //set up data array
  5.         $data = '';
  6.         $i = 1;
  7.         //your basic while loop to get the data
  8.         while($tmp = db_fetch_array($result)){
  9.                 $data[$i] = $tmp;
  10.         }
  11. }

It can be as simple or as complex as you want. We'll cover an example of a customization here.

Removing the ID

Sometimes, you don't want to expose the ID of your element to the user, but you need it to generate the URL of the extras. So, after you have used it in the above code, you can 'unset' it.

Simply insert the following line at the end of the while loop (but still inside it!):

  1. unset($data[$i]['id']);

Now, the ID doesn't need a header, so you don't need to select the ID in your query and you don't have to show the user how the data is ordered.

Step 5: Theme the output

So, now we simply theme the output, and we have a basic table:

  1. theme('table', $headers, $data);

All we are doing is call the theme() function, and passing the type of element to theme, in our case a table.

This calls the underlying theme function for the table element, called theme_table(), and passing the two parameters; the array of headers, and the array of data (both defined earlier in the code).

If a basic table is all you wanted, the there you go.

However, it is very easy to add some really cool features to the table, so if you want to impress people with your Drupal table wizardry, read on!

Step 6: The Pager

Ok, this table is fine, but what if you have 1,000 records? That makes for a veeeeeery long page, and massively inefficient query! Poor database server!

So, we can simply tell Drupal to add a pager to our query, and only return a predefined number of rows at a time:

  1. $limit = 10;
  2. //generate a paged query
  3. $result = pager_query($sql, $limit);
  4.  

Look simple? Good. It is. All we are going is replacing the call to db_query with a call to pager_query, and passing in a limit, or the number of rows to return per page.

If you wanted to be really clever, this value could be set in an administration page, and added here using variable_get (I'll leave that one to you, I wouldn't want to spoil all your fun!).

There is one more thing to do before our pager will work though. We have to theme the output as a paged table.

Don't worry, this isn't hard, and doesn't involve changing much code.

All we do is replace our current call to theme with:

  1.  $output = theme(
  2.         'table',
  3.         $headers,
  4.         $data
  5. );
  6. $output .= theme('pager', NULL, $limit, 0);
  7. print theme('page', $output);
  8.  

What are we doing here?

  1. Calling theme to theme our original table, and storing the result in a variable
  2. Adding the pager to that variable by calling theme, but this time passing in 'pager' as the type, thus calling the underlying function theme_pager, then appending this to our table variable
  3. Finally, we theme the output as a page to render it ready for display.

Simple eh?

Step 7: Tablesort

The final step on the route to Table Zen is the tabel sort. Wouldn't it be nice to be able to sort by a certain field other than the ID, or simply change the order of the table, on the fly?

Well, with a few simple tweaks of our code, we can!

Remember the array of headers we made earlier? Well, now they are going to come in handy.

For tablesort to work, we need to tell it how to sort the data. This is done by using our array of headers. We simply give each header (which is the name of the field in the database) the order by which to sort the data.

In the following example, we'll use the field 'Name'.

So, our header array becomes:

  1. $headers = array(
  2.         array('data' => t('Name'), 'field' => 'name', 'sort'=> 'desc')
  3. );

This will allow us to sort by a field called 'name', and sort it in descending order when clicked on.

Now, we need to append the table sorting SQL to our original SQL:

  1. $sql = "SELECT id, name, state, customer, creator FROM {[table]}";
  2. //add the order by clause
  3. $sql .= tablesort_sql($headers);

Now, we pass this into our call to pager_query, and bingo, we have a sortable, paged table with extra options.

Taking it further

The beauty of Drupal is that you can take this as far as you want. One obvious choice is to add Drag and Drop functionality, or to wrap this up into a theme function, so all this code is added to a page with one call to your own theme function, for example theme('paged_table', $data);.

Please let me know if you found this useful, or if you have taken it further.

In coming weeks I will be covering how to define your own themeable elements as I mentioned above.

Happy coding!

Top