WordPress

How To Create WordPress Admin Tables Using WP_List_Table

WordPress it's not only a CMS it can be used as an application Framework, when it's being used as a framework you will find yourself adding custom data to your database using the $wpdb object and custom tables. When you have this in place within your application you will want a nice way of displaying the data in the admin area of WordPress.

This is exactly like the posts edit screen it shows a table of all your posts, this comes with searching, sorting, filtering and pagination. It will be great if you can some how reuse this functionality to display your own custom data, luckily in WordPress you are able to reuse this functionality with any data and columns you want. Allowing you to send this data to a WordPress class and it will output the data in the same table and functionality as the edit post screen.

example-list-table In this tutorial we are going to investigate using the WP_List_Table class and see how we can use this to display custom data in the WordPress admin area. Currently WordPress uses the WP_List_Table class to display data for all posts, pages, comments, users and plugins.

Creating Plugin

This tutorial we are going to create an example plugin which we can use to demonstrate the use of the WP_List_Table, this plugin will create a new menu item that adds a new page to display the data we define for the table. For this plugin to work we are going to create two PHP classes, one to create the menu page and the other to create the table that will inherit from WP_List_Table.

First we start off by creating a new WordPress plugin, add a folder in your wp-content/plugins folder and create a new file to setup the plugin.

<?php
/*
 * Plugin Name: Paulund WP List Table Example
 * Description: An example of how to use the WP_List_Table class to display data in your WordPress Admin area
 * Plugin URI: http://www.paulund.co.uk
 * Author: Paul Underwood
 * Author URI: http://www.paulund.co.uk
 * Version: 1.0
 * License: GPL2
 */
?>

Next we are going to create a class to add the menu item and the admin page, this will only run in the admin area so we can make sure we only instantiate the class in the admin area by using the is_admin() function.

if(is_admin())
{
    new Paulund_Wp_List_Table();
}

To allow us to create a new menu item we need to add an action to the admin_menu in the constructor of the Paulund_Wp_List_Table class.

/**
 * Paulund_Wp_List_Table class will create the page to load the table
 */
class Paulund_Wp_List_Table
{
    /**
     * Constructor will create the menu item
     */
    public function __construct()
    {
        add_action( 'admin_menu', array($this, 'add_menu_example_list_table_page' ));
    }
}

The admin_menu action will call the method add_menu_example_list_table_page(), we will use this method to add a new menu page to the admin menu.

/**
     * Menu item will allow us to load the page to display the table
     */
    public function add_menu_example_list_table_page()
    {
        add_menu_page( 'Example List Table', 'Example List Table', 'manage_options', 'example-list-table.php', array($this, 'list_table_page') );
    }

On creating this new menu item we define a callback function to run when you navigate to this new admin page, this will run the list_table_page() which is where we will add the code to display the table.

/**
     * Display the list table page
     *
     * @return Void
     */
    public function list_table_page()
    {
        $exampleListTable = new Example_List_Table();
        $exampleListTable->prepare_items();
        ?>
            <div class="wrap">
                <div id="icon-users" class="icon32"></div>
                <h2>Example List Table Page</h2>
                <?php $exampleListTable->display(); ?>
            </div>
        <?php
    }

In the list_table_page method we instantiate the class Example_List_Table which is where we define how to show the data in the table, after this is instantiated we call the method prepare_items() in here we define what columns to show, hidden columns and sortable columns. This method will also define the data to show and the order of the data in the table.

This is all we need to display the table on the admin pages, all the work will be handled in the Example_List_Table, the next part of the tutorial will explain different parts of this class and how we use this to display data.

Creating The Example_List_Table Class

The Example_List_Table class is going to inherit the WP_List_Table class so it will have access to everything this can do, we will use this class to define the data and the columns.

To use the WP_List_Table class you first need to include it in your application so that you can make sure we inherit from this class.

// WP_List_Table is not loaded automatically so we need to load it in our application
if( ! class_exists( 'WP_List_Table' ) ) {
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

/**
 * Create a new table class that will extend the WP_List_Table
 */
class Example_List_Table extends WP_List_Table
{

}

There are many things this class will do:

  • Set the columns
  • Define hidden columns
  • Define sortable columns
  • Define what each column data is shown
  • Prepare the table items
  • Define the data
  • Sort data function

Prepare The Table

The first method that we call from the Example_List_Table class is the prepare_items() method, inside this method we need to define the columns, hidden columns, sortable columns, data, and sorting.

/**
     * Prepare the items for the table to process
     *
     * @return Void
     */
    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = $this->get_hidden_columns();
        $sortable = $this->get_sortable_columns();

        $data = $this->table_data();

        $this->_column_headers = array($columns, $hidden, $sortable);
        $this->items = $data;
    }

It's important to set the class variables _column_headers and items as these are used inside the parent class to display the table. As you can see from this code above there are three methods at the top to get the columns, hidden columns and sortable columns. After this we call the table_data() method to return the array of data to display in the table.

Get Columns

Within the Example_List_Table class we need to create the method get_columns() which will override the method on the parent class, this method must return an array of columns that are going to be used in your table.

/**
     * Override the parent columns method. Defines the columns to use in your listing table
     *
     * @return Array
     */
    public function get_columns()
    {
        $columns = array(
            'id'          => 'ID',
            'title'       => 'Title',
            'description' => 'Description',
            'year'        => 'Year',
            'director'    => 'Director',
            'rating'      => 'Rating'
        );

        return $columns;
    }

Hidden Columns

If you want to hide certain columns from the table you will define them here.

/**
     * Define which columns are hidden
     *
     * @return Array
     */
    public function get_hidden_columns()
    {
        return array();
    }

This method expects you to return an array of all hidden column keys which you define in the get_columns() method. For example if I wanted to hide the year and director columns I will use the following code.

/**
     * Define which columns are hidden
     *
     * @return Array
     */
    public function get_hidden_columns()
    {
        return array('year', 'director');
    }

Sorting Columns

By defining which columns you want people to sort on, WordPress will add a link around the title of the column so you can click this title and change the order from ASC to DESC.

/**
     * Define the sortable columns
     *
     * @return Array
     */
    public function get_sortable_columns()
    {
        return array('title' => array('title', false));
    }

The return from this method should be an array and you will need to define each column you want to sort on. The false in this example will tell WordPress that this column will start off unordered, if this is set to true then you are telling WordPress this column will start off ordered ASC.

Set Data

The data that the table expects should be an array of arrays with key value pairs of the column name and the value you want to display. In this example I am using the top 10 movies on IMDB to display information in the table.

/**
     * Get the table data
     *
     * @return Array
     */
    private function table_data()
    {
        $data = array();

        $data[] = array(
                    'id'          => 1,
                    'title'       => 'The Shawshank Redemption',
                    'description' => 'Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.',
                    'year'        => '1994',
                    'director'    => 'Frank Darabont',
                    'rating'      => '9.3'
                    );

        $data[] = array(
                    'id'          => 2,
                    'title'       => 'The Godfather',
                    'description' => 'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.',
                    'year'        => '1972',
                    'director'    => 'Francis Ford Coppola',
                    'rating'      => '9.2'
                    );

        $data[] = array(
                    'id'          => 3,
                    'title'       => 'The Godfather: Part II',
                    'description' => 'The early life and career of Vito Corleone in 1920s New York is portrayed while his son, Michael, expands and tightens his grip on his crime syndicate stretching from Lake Tahoe, Nevada to pre-revolution 1958 Cuba.',
                    'year'        => '1974',
                    'director'    => 'Francis Ford Coppola',
                    'rating'      => '9.0'
                    );

        $data[] = array(
                    'id'          => 4,
                    'title'       => 'Pulp Fiction',
                    'description' => 'The lives of two mob hit men, a boxer, a gangster\'s wife, and a pair of diner bandits intertwine in four tales of violence and redemption.',
                    'year'        => '1994',
                    'director'    => 'Quentin Tarantino',
                    'rating'      => '9.0'
                    );

        $data[] = array(
                    'id'          => 5,
                    'title'       => 'The Good, the Bad and the Ugly',
                    'description' => 'A bounty hunting scam joins two men in an uneasy alliance against a third in a race to find a fortune in gold buried in a remote cemetery.',
                    'year'        => '1966',
                    'director'    => 'Sergio Leone',
                    'rating'      => '9.0'
                    );

        $data[] = array(
                    'id'          => 6,
                    'title'       => 'The Dark Knight',
                    'description' => 'When Batman, Gordon and Harvey Dent launch an assault on the mob, they let the clown out of the box, the Joker, bent on turning Gotham on itself and bringing any heroes down to his level.',
                    'year'        => '2008',
                    'director'    => 'Christopher Nolan',
                    'rating'      => '9.0'
                    );

        $data[] = array(
                    'id'          => 7,
                    'title'       => '12 Angry Men',
                    'description' => 'A dissenting juror in a murder trial slowly manages to convince the others that the case is not as obviously clear as it seemed in court.',
                    'year'        => '1957',
                    'director'    => 'Sidney Lumet',
                    'rating'      => '8.9'
                    );

        $data[] = array(
                    'id'          => 8,
                    'title'       => 'Schindler\'s List',
                    'description' => 'In Poland during World War II, Oskar Schindler gradually becomes concerned for his Jewish workforce after witnessing their persecution by the Nazis.',
                    'year'        => '1993',
                    'director'    => 'Steven Spielberg',
                    'rating'      => '8.9'
                    );

        $data[] = array(
                    'id'          => 9,
                    'title'       => 'The Lord of the Rings: The Return of the King',
                    'description' => 'Gandalf and Aragorn lead the World of Men against Sauron\'s army to draw his gaze from Frodo and Sam as they approach Mount Doom with the One Ring.',
                    'year'        => '2003',
                    'director'    => 'Peter Jackson',
                    'rating'      => '8.9'
                    );

        $data[] = array(
                    'id'          => 10,
                    'title'       => 'Fight Club',
                    'description' => 'An insomniac office worker looking for a way to change his life crosses paths with a devil-may-care soap maker and they form an underground fight club that evolves into something much, much more...',
                    'year'        => '1999',
                    'director'    => 'David Fincher',
                    'rating'      => '8.8'
                    );

        return $data;
    }

As you can see we have created a new variable of $data which is an array, then we add a new item to this which is an array of the key value pair for the movie information. You need to make sure that the key indexes are the same as the keys you have on the columns array.

Set The Column Data

So far we have defined the columns and defined the data we can use but now we need to define what data each column will display. There are two ways of doing this you can create a method for each column column_{column_key_name}().

// Used to display the value of the id column
public function column_id($item)
{
    return $item['id'];
}

The other option we can use to display the column values is by creating a method column_default(), this will get called if the specific column method doesn't exist and can be used for all columns if you are not doing anything different with them. In this example we are just displaying the value in each column so we can use the column_default() method to display all the columns.

/**
     * Define what data to show on each column of the table
     *
     * @param  Array $item        Data
     * @param  String $column_name - Current column name
     *
     * @return Mixed
     */
    public function column_default( $item, $column_name )
    {
        switch( $column_name ) {
            case 'id':
            case 'title':
            case 'description':
            case 'year':
            case 'director':
            case 'rating':
                return $item[ $column_name ];

            default:
                return print_r( $item, true ) ;
        }
    }

This is all the code we need to display the table, if you install the plugin and load up your admin area you will see the table with the data like below.

example-list-table

But at the current time the table has no interaction, you can't search on it, sort the data or paginate large data sets.

Sorting Data

To sort our data, you need to define which columns are sortable in the get_sortable_columns() method, then we need to create a function that will perform the sorting, so we can use the usort function to sort the data on a certain column.

/**
     * Allows you to sort the data by the variables set in the $_GET
     *
     * @return Mixed
     */
    private function sort_data( $a, $b )
    {
        // Set defaults
        $orderby = 'title';
        $order = 'asc';

        // If orderby is set, use this as the sort column
        if(!empty($_GET['orderby']))
        {
            $orderby = $_GET['orderby'];
        }

        // If order is set use this as the order
        if(!empty($_GET['order']))
        {
            $order = $_GET['order'];
        }

        $result = strnatcmp( $a[$orderby], $b[$orderby] );

        if($order === 'asc')
        {
            return $result;
        }

        return -$result;
    }

This method sets a default column (title) and default order (ASC) for the sorting, then we search the $_GET variable for the new sorting column to return the data in the correct order.

Once this method is created we then need to call it in the prepare_items() method after we define the data to use, we do this by using the usort function and then we can continue using the $data variable.

/**
     * Prepare the items for the table to process
     *
     * @return Void
     */
    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = $this->get_hidden_columns();
        $sortable = $this->get_sortable_columns();

        $data = $this->table_data();
        usort( $data, array( &$this, 'sort_data' ) );

        $this->_column_headers = array($columns, $hidden, $sortable);
        $this->items = $data;
    }

Pagination

If you are working with large data sets then you will want to add pagination to your tables. But WordPress doesn't actually paginate your data but will allow you to pass in a page number to use array_slice to return bits of your data.

To add pagination we need to change the prepare_items() method, in here we need to define how many items we want to return per page and be able to get the page number by using $this->get_pagenum(), then we use the array_slice function to return only the data for this page.

The parent class needs this information by passing in the arguments into the set_pagination_args() method.

/**
     * Prepare the items for the table to process
     *
     * @return Void
     */
    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = $this->get_hidden_columns();
        $sortable = $this->get_sortable_columns();

        $data = $this->table_data();
        usort( $data, array( &$this, 'sort_data' ) );

        $perPage = 20;
        $currentPage = $this->get_pagenum();
        $totalItems = count($data);

        $this->set_pagination_args( array(
            'total_items' => $totalItems,
            'per_page'    => $perPage
        ) );

        $data = array_slice($data,(($currentPage-1)*$perPage),$perPage);

        $this->_column_headers = array($columns, $hidden, $sortable);
        $this->items = $data;
    }

Searching Data

With really large bits of data you may need to search through this to find what you need, there is a method on the WP_List_Table class which allows you to easily add a search box to the page.

Using the method search_box() on the WP_List_Table will output a search box and a search button to allow you to search the data in your table.

You will need to change the list_table_page() method as this search box will be added to the page, you just need to add a search form where you want the search box to be displayed.

<form method="post">
    <input type="hidden" name="page" value="example_list_table" />
    <?php $exampleListTable->search_box('search', 'search_id'); ?>
</form>

Now we will have access to the $_GET['s'] variable to return the data that we need.

Full Plugin Code

Below is the full code for the plugin, use this as an example when you need to use the WP_List_Table.

<?php
/*
 * Plugin Name: Paulund WP List Table Example
 * Description: An example of how to use the WP_List_Table class to display data in your WordPress Admin area
 * Plugin URI: http://www.paulund.co.uk
 * Author: Paul Underwood
 * Author URI: http://www.paulund.co.uk
 * Version: 1.0
 * License: GPL2
 */
 
if(is_admin())
{
    new Paulund_Wp_List_Table();
}
 
/**
 * Paulund_Wp_List_Table class will create the page to load the table
 */
class Paulund_Wp_List_Table
{
    /**
     * Constructor will create the menu item
     */
    public function __construct()
    {
        add_action( 'admin_menu', array($this, 'add_menu_example_list_table_page' ));
    }
 
    /**
     * Menu item will allow us to load the page to display the table
     */
    public function add_menu_example_list_table_page()
    {
        add_menu_page( 'Example List Table', 'Example List Table', 'manage_options', 'example-list-table.php', array($this, 'list_table_page') );
    }
 
    /**
     * Display the list table page
     *
     * @return Void
     */
    public function list_table_page()
    {
        $exampleListTable = new Example_List_Table();
        $exampleListTable->prepare_items();
        ?>
            <div class="wrap">
                <div id="icon-users" class="icon32"></div>
                <h2>Example List Table Page</h2>
                <?php $exampleListTable->display(); ?>
            </div>
        <?php
    }
}
 
// WP_List_Table is not loaded automatically so we need to load it in our application
if( ! class_exists( 'WP_List_Table' ) ) {
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
 
/**
 * Create a new table class that will extend the WP_List_Table
 */
class Example_List_Table extends WP_List_Table
{
    /**
     * Prepare the items for the table to process
     *
     * @return Void
     */
    public function prepare_items()
    {
        $columns = $this->get_columns();
        $hidden = $this->get_hidden_columns();
        $sortable = $this->get_sortable_columns();
 
        $data = $this->table_data();
        usort( $data, array( &$this, 'sort_data' ) );
 
        $perPage = 20;
        $currentPage = $this->get_pagenum();
        $totalItems = count($data);
 
        $this->set_pagination_args( array(
            'total_items' => $totalItems,
            'per_page'    => $perPage
        ) );
 
        $data = array_slice($data,(($currentPage-1)*$perPage),$perPage);
 
        $this->_column_headers = array($columns, $hidden, $sortable);
        $this->items = $data;
    }
 
    /**
     * Override the parent columns method. Defines the columns to use in your listing table
     *
     * @return Array
     */
    public function get_columns()
    {
        $columns = array(
            'id'          => 'ID',
            'title'       => 'Title',
            'description' => 'Description',
            'year'        => 'Year',
            'director'    => 'Director',
            'rating'      => 'Rating'
        );
 
        return $columns;
    }
 
    /**
     * Define which columns are hidden
     *
     * @return Array
     */
    public function get_hidden_columns()
    {
        return array();
    }
 
    /**
     * Define the sortable columns
     *
     * @return Array
     */
    public function get_sortable_columns()
    {
        return array('title' => array('title', false));
    }
 
    /**
     * Get the table data
     *
     * @return Array
     */
    private function table_data()
    {
        $data = array();
 
        $data[] = array(
                    'id'          => 1,
                    'title'       => 'The Shawshank Redemption',
                    'description' => 'Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.',
                    'year'        => '1994',
                    'director'    => 'Frank Darabont',
                    'rating'      => '9.3'
                    );
 
        $data[] = array(
                    'id'          => 2,
                    'title'       => 'The Godfather',
                    'description' => 'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.',
                    'year'        => '1972',
                    'director'    => 'Francis Ford Coppola',
                    'rating'      => '9.2'
                    );
 
        $data[] = array(
                    'id'          => 3,
                    'title'       => 'The Godfather: Part II',
                    'description' => 'The early life and career of Vito Corleone in 1920s New York is portrayed while his son, Michael, expands and tightens his grip on his crime syndicate stretching from Lake Tahoe, Nevada to pre-revolution 1958 Cuba.',
                    'year'        => '1974',
                    'director'    => 'Francis Ford Coppola',
                    'rating'      => '9.0'
                    );
 
        $data[] = array(
                    'id'          => 4,
                    'title'       => 'Pulp Fiction',
                    'description' => 'The lives of two mob hit men, a boxer, a gangster\'s wife, and a pair of diner bandits intertwine in four tales of violence and redemption.',
                    'year'        => '1994',
                    'director'    => 'Quentin Tarantino',
                    'rating'      => '9.0'
                    );
 
        $data[] = array(
                    'id'          => 5,
                    'title'       => 'The Good, the Bad and the Ugly',
                    'description' => 'A bounty hunting scam joins two men in an uneasy alliance against a third in a race to find a fortune in gold buried in a remote cemetery.',
                    'year'        => '1966',
                    'director'    => 'Sergio Leone',
                    'rating'      => '9.0'
                    );
 
        $data[] = array(
                    'id'          => 6,
                    'title'       => 'The Dark Knight',
                    'description' => 'When Batman, Gordon and Harvey Dent launch an assault on the mob, they let the clown out of the box, the Joker, bent on turning Gotham on itself and bringing any heroes down to his level.',
                    'year'        => '2008',
                    'director'    => 'Christopher Nolan',
                    'rating'      => '9.0'
                    );
 
        $data[] = array(
                    'id'          => 7,
                    'title'       => '12 Angry Men',
                    'description' => 'A dissenting juror in a murder trial slowly manages to convince the others that the case is not as obviously clear as it seemed in court.',
                    'year'        => '1957',
                    'director'    => 'Sidney Lumet',
                    'rating'      => '8.9'
                    );
 
        $data[] = array(
                    'id'          => 8,
                    'title'       => 'Schindler\'s List',
                    'description' => 'In Poland during World War II, Oskar Schindler gradually becomes concerned for his Jewish workforce after witnessing their persecution by the Nazis.',
                    'year'        => '1993',
                    'director'    => 'Steven Spielberg',
                    'rating'      => '8.9'
                    );
 
        $data[] = array(
                    'id'          => 9,
                    'title'       => 'The Lord of the Rings: The Return of the King',
                    'description' => 'Gandalf and Aragorn lead the World of Men against Sauron\'s army to draw his gaze from Frodo and Sam as they approach Mount Doom with the One Ring.',
                    'year'        => '2003',
                    'director'    => 'Peter Jackson',
                    'rating'      => '8.9'
                    );
 
        $data[] = array(
                    'id'          => 10,
                    'title'       => 'Fight Club',
                    'description' => 'An insomniac office worker looking for a way to change his life crosses paths with a devil-may-care soap maker and they form an underground fight club that evolves into something much, much more...',
                    'year'        => '1999',
                    'director'    => 'David Fincher',
                    'rating'      => '8.8'
                    );
 
        return $data;
    }
 
    /**
     * Define what data to show on each column of the table
     *
     * @param  Array $item        Data
     * @param  String $column_name - Current column name
     *
     * @return Mixed
     */
    public function column_default( $item, $column_name )
    {
        switch( $column_name ) {
            case 'id':
            case 'title':
            case 'description':
            case 'year':
            case 'director':
            case 'rating':
                return $item[ $column_name ];
 
            default:
                return print_r( $item, true ) ;
        }
    }
 
    /**
     * Allows you to sort the data by the variables set in the $_GET
     *
     * @return Mixed
     */
    private function sort_data( $a, $b )
    {
        // Set defaults
        $orderby = 'title';
        $order = 'asc';
 
        // If orderby is set, use this as the sort column
        if(!empty($_GET['orderby']))
        {
            $orderby = $_GET['orderby'];
        }
 
        // If order is set use this as the order
        if(!empty($_GET['order']))
        {
            $order = $_GET['order'];
        }
 
 
        $result = strnatcmp( $a[$orderby], $b[$orderby] );
 
        if($order === 'asc')
        {
            return $result;
        }
 
        return -$result;
    }
}
?>
Back to top

Fastest WordPress Hosting With WPEngine

Stunning speed, powerful security, and best-in-class customer service. At WP Engine.

Risk free for 60 days