I'm developing a WordPress plugin that will display a publicly accessible noticeboard on a page.

I've been asked to add the means to delete existing posts from the noticeboard using the WordPress admin interface, and I've used this tutorial for guidance on creating the table to hold the details of each entry.

It's working to the extent that I can display the table and move between pages, but there's rather an odd error with the sorting. The hyperlink for sorting by each individual column only shows the first character of the column name in the orderby parameter, thus breaking this functionality.

For instance, the link for sorting by the description is


and should be


... which works.

Now, I've taken a look in wp-admin/includes/class-wp-list-table.php, in the print_column_headers method, and the problem appears to arise on line 679, which reads as follows:

list( $orderby, $desc_first ) = $sortable[$column_key];

If I do a var_dump of $sortable[$column_key], I get exactly the value I would expect, which in this case is a string with a value of "title", since that's the first column I want to show. But the actual values held by $orderby and $desc_first are "t" and "i", so it appears list() is treating $sortable[$column_key] as an array and returning the first two elements.

I'm assuming this isn't a bug in WordPress as I could not find any mention of a bug like this in the version of WordPress I'm using for development purposes, so I think the most likely explanation is that I've done something wrong somewhere along the line. It looks like where I define the get_sortable_columns method, I should be passing through an array containing two or more values as the value for each element in the $sortable associative array.

Here's the code I've written for the admin:

// Retrieve any recaptcha keys sent via POST
    update_option('recaptcha_public_key', $_POST['recaptcha_public_key']);

    update_option('recaptcha_private_key', $_POST['recaptcha_private_key']);

// Create a class for the post list table
class Post_List_Table extends WP_List_Table {
     * Constructor
    function __construct() {
        parent::__construct( array(
            'singular'=> 'wp_list_post',
            'plural' => 'wp_list_posts',
            'ajax' => false
        ) );

    function get_columns() {
        return $columns = array(
            'submissionDate'=>__('Date submitted'),
            'name'=>__('Author name'),
            'email'=>__('Author email'),
            'post_title'=>__('Page this has been posted on')

    public function get_sortable_columns() {
        return $sortable = array(

    function prepare_items() {
        global $wpdb, $_wp_column_headers;
        $screen = get_current_screen();

        // Prepare query
        $query = "SELECT ".$wpdb->prefix."noticeboard_posts.title, ".$wpdb->prefix."noticeboard_posts.description, ".$wpdb->prefix."noticeboard_posts.submissionDate, ".$wpdb->prefix."noticeboard_author.name, ".$wpdb->prefix."noticeboard_author.email, ".$wpdb->prefix."posts.post_title FROM ".$wpdb->prefix."noticeboard_posts INNER JOIN ".$wpdb->prefix."noticeboard_author ON ".$wpdb->prefix."noticeboard_posts.authorId = ".$wpdb->prefix."noticeboard_author.authorId INNER JOIN ".$wpdb->prefix."posts ON ".$wpdb->prefix."noticeboard_posts.pageID = ".$wpdb->prefix."posts.ID";

        // Parameters that are going to be used to order the result
        $orderby = !empty($_GET["orderby"]) ? mysql_real_escape_string($_GET["orderby"]) : 'ASC';
        $order = !empty($_GET["order"]) ? mysql_real_escape_string($_GET["order"]) : '';
        if(!empty($orderby) & !empty($order)){ $query.=' ORDER BY '.$orderby.' '.$order; }

        // Parameters for pagination
        $totalitems = $wpdb->query($query);

        // How many to display per page?
        $perpage = 5;

        // Which page is this?
        $paged = !empty($_GET["paged"]) ? mysql_real_escape_string($_GET["paged"]) : '';

        // Page number
        if(empty($paged) || !is_numeric($paged) || $paged<=0 ){ $paged=1; }

        // How many pages do we have in total?
        $totalpages = ceil($totalitems/$perpage);

        //adjust the query to take pagination into account
        if(!empty($paged) && !empty($perpage)){
            $paged = (int)$paged;
            $offset = ($paged - 1) * $perpage;
            $query .=' LIMIT '.(int)$offset.', '.(int)$perpage;

        // Register the pagination
        $this->set_pagination_args( array(
            "total_items" => $totalitems,
            "total_pages" => $totalpages,
            "per_page" => $perpage,
        ) );

        // The pagination links are automatically built according to those parameters

        // Register the columns
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = $this->get_sortable_columns();
        $this->_column_headers = array($columns, $hidden, $sortable);

        // Fetch the items
        $this->items = $wpdb->get_results($query);

    function display_rows() {

        // Get the records registered in the prepare_items method
        $records = $this->items;

        // Get the columns registered in the get_columns and get_sortable_columns methods
        list ( $columns, $hidden ) = $this->get_column_info();

        // Loop for each record
        if(!empty($records)){foreach($records as $rec){

            // Open the line
            echo '<tr id="record_'.$rec->postId.'">';
            foreach ( $columns as $column_name => $column_display_name ) {

                // Style attributes for each column
                $class = "class='$column_name column-$column_name'";
                $style = "";
                if ( in_array( $column_name, $hidden ) ) $style = ' style="display:none;"';
                $attributes = $class . $style;

                //Display the cell
                switch ( $column_name ) {
                case "title": echo '<td '.$attributes.'><strong>'.stripslashes($rec->title).'</strong></td>';  break;
                case "description": echo '<td '.$attributes.'>'.stripslashes($rec->description).'</td>'; break;
                case "submissionDate": echo '<td '.$attributes.'>'.stripslashes(date("j F Y", strtotime($rec->submissionDate))).'</td>'; break;
                case "name": echo '<td '.$attributes.'>'.$rec->name.'</td>'; break;
                case "email": echo '<td '.$attributes.'>'.$rec->email.'</td>'; break;
                case "post_title": echo '<td '.$attributes.'>'.$rec->post_title.'</td>'; break;

        //Close the line
        echo '</tr>';
<div class="wrap">
    <h2>Noticeboard administration</h2>
    <form name="notification_admin_form" method="post" action="<?php echo str_replace( '%7E', '~', $_SERVER['REQUEST_URI']); ?>">
        <p><label>reCAPTCHA public key <input type="text" name="recaptcha_public_key" size="60" value="<?php echo get_option('recaptcha_public_key', '');?>" /></label></p>
        <p><label>reCAPTCHA private key <input type="text" name="recaptcha_private_key" size="60" value="<?php echo get_option('recaptcha_private_key', '');?>" /></label></p>
        <input type="submit" name="Submit" value="Update reCAPTCHA key details" />
<h2>Edit posts</h2>

//Our class extends the WP_List_Table class, so we need to make sure that it's there
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );

//Prepare Table of elements
$wp_post_table = new Post_List_Table();

//Table of elements

Any ideas as to where I might have gone wrong?

  • 1
    Btw: Have you read about the List Table API on Codex? – kaiser Jun 28 '12 at 11:51
  • @kaiser Aha! I had skimmed the page you linked to on the Codex and I missed this section which explains how these work. I've changed it accordingly and it now works fine. Guess that tutorial's either out of date or plain incorrect. – mattbd Jun 28 '12 at 12:01
  • Then add this as answer with your new code. – kaiser Jun 28 '12 at 12:02
  • 1
    @kaiser Will do. Cheers. – mattbd Jun 28 '12 at 12:03

Figured this out myself after kaiser pointed me towards the appropriate entry on the Codex, specifically the part at http://codex.wordpress.org/Class_Reference/WP_List_Table#Notice:_Sortable_Columns which I'd managed to miss. Problem was with the get_sortable_columns method, which should look like this:

    public function get_sortable_columns() {
    return $sortable = array(

Guess the tutorial I was using must have been incorrect.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.