Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Sample plugin for usage of WP_List_Table class (complete version)
<?php
/*
Plugin Name: Test List Table Example
*/
if( ! class_exists( 'WP_List_Table' ) ) {
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
class My_Example_List_Table extends WP_List_Table {
var $example_data = array(
array( 'ID' => 1,'booktitle' => 'Quarter Share', 'author' => 'Nathan Lowell',
'isbn' => '978-0982514542' ),
array( 'ID' => 2, 'booktitle' => '7th Son: Descent','author' => 'J. C. Hutchins',
'isbn' => '0312384378' ),
array( 'ID' => 3, 'booktitle' => 'Shadowmagic', 'author' => 'John Lenahan',
'isbn' => '978-1905548927' ),
array( 'ID' => 4, 'booktitle' => 'The Crown Conspiracy', 'author' => 'Michael J. Sullivan',
'isbn' => '978-0979621130' ),
array( 'ID' => 5, 'booktitle' => 'Max Quick: The Pocket and the Pendant', 'author' => 'Mark Jeffrey',
'isbn' => '978-0061988929' ),
array('ID' => 6, 'booktitle' => 'Jack Wakes Up: A Novel', 'author' => 'Seth Harwood',
'isbn' => '978-0307454355' )
);
function __construct(){
global $status, $page;
parent::__construct( array(
'singular' => __( 'book', 'mylisttable' ), //singular name of the listed records
'plural' => __( 'books', 'mylisttable' ), //plural name of the listed records
'ajax' => false //does this table support ajax?
) );
add_action( 'admin_head', array( &$this, 'admin_header' ) );
}
function admin_header() {
$page = ( isset($_GET['page'] ) ) ? esc_attr( $_GET['page'] ) : false;
if( 'my_list_test' != $page )
return;
echo '<style type="text/css">';
echo '.wp-list-table .column-id { width: 5%; }';
echo '.wp-list-table .column-booktitle { width: 40%; }';
echo '.wp-list-table .column-author { width: 35%; }';
echo '.wp-list-table .column-isbn { width: 20%;}';
echo '</style>';
}
function no_items() {
_e( 'No books found, dude.' );
}
function column_default( $item, $column_name ) {
switch( $column_name ) {
case 'booktitle':
case 'author':
case 'isbn':
return $item[ $column_name ];
default:
return print_r( $item, true ) ; //Show the whole array for troubleshooting purposes
}
}
function get_sortable_columns() {
$sortable_columns = array(
'booktitle' => array('booktitle',false),
'author' => array('author',false),
'isbn' => array('isbn',false)
);
return $sortable_columns;
}
function get_columns(){
$columns = array(
'cb' => '<input type="checkbox" />',
'booktitle' => __( 'Title', 'mylisttable' ),
'author' => __( 'Author', 'mylisttable' ),
'isbn' => __( 'ISBN', 'mylisttable' )
);
return $columns;
}
function usort_reorder( $a, $b ) {
// If no sort, default to title
$orderby = ( ! empty( $_GET['orderby'] ) ) ? $_GET['orderby'] : 'booktitle';
// If no order, default to asc
$order = ( ! empty($_GET['order'] ) ) ? $_GET['order'] : 'asc';
// Determine sort order
$result = strcmp( $a[$orderby], $b[$orderby] );
// Send final sort direction to usort
return ( $order === 'asc' ) ? $result : -$result;
}
function column_booktitle($item){
$actions = array(
'edit' => sprintf('<a href="?page=%s&action=%s&book=%s">Edit</a>',$_REQUEST['page'],'edit',$item['ID']),
'delete' => sprintf('<a href="?page=%s&action=%s&book=%s">Delete</a>',$_REQUEST['page'],'delete',$item['ID']),
);
return sprintf('%1$s %2$s', $item['booktitle'], $this->row_actions($actions) );
}
function get_bulk_actions() {
$actions = array(
'delete' => 'Delete'
);
return $actions;
}
function column_cb($item) {
return sprintf(
'<input type="checkbox" name="book[]" value="%s" />', $item['ID']
);
}
function prepare_items() {
$columns = $this->get_columns();
$hidden = array();
$sortable = $this->get_sortable_columns();
$this->_column_headers = array( $columns, $hidden, $sortable );
usort( $this->example_data, array( &$this, 'usort_reorder' ) );
$per_page = 5;
$current_page = $this->get_pagenum();
$total_items = count( $this->example_data );
// only ncessary because we have sample data
$this->found_data = array_slice( $this->example_data,( ( $current_page-1 )* $per_page ), $per_page );
$this->set_pagination_args( array(
'total_items' => $total_items, //WE have to calculate the total number of items
'per_page' => $per_page //WE have to determine how many items to show on a page
) );
$this->items = $this->found_data;
}
} //class
function my_add_menu_items(){
$hook = add_menu_page( 'My Plugin List Table', 'My List Table Example', 'activate_plugins', 'my_list_test', 'my_render_list_page' );
add_action( "load-$hook", 'add_options' );
}
function add_options() {
global $myListTable;
$option = 'per_page';
$args = array(
'label' => 'Books',
'default' => 10,
'option' => 'books_per_page'
);
add_screen_option( $option, $args );
$myListTable = new My_Example_List_Table();
}
add_action( 'admin_menu', 'my_add_menu_items' );
function my_render_list_page(){
global $myListTable;
echo '</pre><div class="wrap"><h2>My List Table Test</h2>';
$myListTable->prepare_items();
?>
<form method="post">
<input type="hidden" name="page" value="ttest_list_table">
<?php
$myListTable->search_box( 'search', 'search_id' );
$myListTable->display();
echo '</form></div>';
}
@emeraldjava

This comment has been minimized.

Copy link

commented Aug 15, 2012

Hi,
Can you explain where the 'edit' and 'delete' url actions are handled?

@lgt

This comment has been minimized.

Copy link

commented Dec 10, 2012

I'm interested as well!

@atwellpub

This comment has been minimized.

Copy link

commented Jan 17, 2013

I'm in need of this too for now. Any leads?

@GwynethLlewelyn

This comment has been minimized.

Copy link

commented Apr 21, 2014

For deletion, before line 169, where the <form post> begins, you should test for something like:

`if ($_POST['action'] == 'delete' || $_POST['action2'] == 'delete'])
... handle items to be deleted, iterating through $book (which will have the IDs of the items to delete)
else

...`

If you wish to handle more complicated actions, then these should all be tested before the final else.

@GwynethLlewelyn

This comment has been minimized.

Copy link

commented Apr 21, 2014

Hmm. A neater way is to use process_bulk_action:

function process_bulk_action() { //Detect when a bulk action is being triggered... if( 'delete'===$this->current_action() ) { wp_die('Items deleted (or they would be if we had items to delete)!'); }

And then, inside the method prepare_items, use:

$this->process_bulk_action();

@sweetnight

This comment has been minimized.

Copy link

commented Oct 17, 2014

This class (or child-class) only generates $_POST and $_GET. Those generated global variables can be used to process any kind of commands. In example, you can use them to load data from SQL tables, or you can use them to bring user to another page for editing, etc.

You can use those variables in a same page along with your tables, or you can use them in another page.

In wordpress, I always use 'init' (and 'admin_init') action to process those variables. As the codex mentioned that the best use of 'init' action is to intercept $_GET or $_POST variables that are generated from a page.

Maybe these resources can help :
http://www.w3schools.com/php/php_forms.asp
http://www.cmsws.com/?page_id=5
http://codex.wordpress.org/Plugin_API/Action_Reference/init
http://codex.wordpress.org/Plugin_API/Action_Reference/admin_init

Also it's a best practice if you read the step-by-step guide of this class that is written by the code writer 💃
http://wpengineer.com/2426/wp_list_table-a-step-by-step-guide/

@a00andos

This comment has been minimized.

Copy link

commented Jun 1, 2017

I needed a declaration of the $found_data variable inside the class to make the pagination work.
So i added this row at the top of the class: public $found_data = array();

@awps

This comment has been minimized.

Copy link

commented Sep 6, 2017

Check out this example plugin if you are looking for a well organized and commented code: https://wordpress.org/plugins/custom-list-table-example/

@SeanDS

This comment has been minimized.

Copy link

commented Mar 5, 2019

The books_per_page screen option doesn't work correctly here, because, as with most of this example code, it has been copied and pasted all over the web and not actually tested. WordPress does not save this setting by default; if you try to set the items per page in the screen options it will simply ignore what you type and use the default. To get this to work you must add a filter to set-screen-option. Here is an example of how to do that. Furthermore, remember to prefix the setting name because you can't guarantee other plugins won't use the same slug. And, of course, delete it and any other user meta you set in your plugin's uninstall code.

@amjad

This comment has been minimized.

Copy link

commented Jun 5, 2019

How do I show this table in the Dashboard page? Or does dashboard only show widgets?

@Latz

This comment has been minimized.

Copy link
Owner Author

commented Jun 5, 2019

@SeanDS:

The books_per_page screen option doesn't work correctly here, because, as with most of this example code, it has been copied and pasted all over the web and not actually tested.

Yes I have copied some (a lot) of the code, but not from the web (there have been no examples a the time, therefore I wrote the tutorial) but from the core code. and simplified it. Maybe I did to test some parts.

WordPress does not save this setting by default; if you try to set the items per page in the screen options it will simply ignore what you type and use the default. To get this to work you must add a filter to set-screen-option. Here is an example of how to do that.

Thanks for the clarification.

Furthermore, remember to prefix the setting name because you can't guarantee other plugins won't use the same slug. And, of course, delete it and any other user meta you set in your plugin's uninstall code.

Of course you need do this and it's good practise. In my mind tutorial should use as few characters as possible to focus on the main problem.

Thanks for your comments, users of this code will appreciate it.

@Latz

This comment has been minimized.

Copy link
Owner Author

commented Jun 5, 2019

@amjad

How do I show this table in the Dashboard page? Or does dashboard only show widgets?

You need to create a widget to use it in the Dashboard but you can use the WP_list_Table inside that widget.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.