利用 WP_List_Table
类构建复杂的后台数据列表
大家好,今天我们来深入探讨如何使用 WordPress 的 WP_List_Table
类来构建复杂的后台数据列表。WP_List_Table
提供了一个强大的框架,允许我们以结构化和可定制的方式在 WordPress 后台展示数据。虽然它本身可能有些复杂,但掌握了它之后,就能创建出功能丰富的管理界面。
1. WP_List_Table
的基础
WP_List_Table
是一个抽象类,这意味着我们不能直接实例化它。我们需要创建一个子类并实现一些必要的方法,才能使用它。
首先,我们需要包含 WP_List_Table
类。通常,WordPress 不会自动加载它,因此我们需要手动包含:
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
这段代码检查 WP_List_Table
是否已经定义,如果没有,则包含 WordPress 核心文件 class-wp-list-table.php
。
2. 创建自定义列表类
现在,让我们创建一个自定义类,继承 WP_List_Table
:
class My_Custom_List_Table extends WP_List_Table {
function __construct() {
global $status, $page;
parent::__construct( array(
'singular' => 'book', // 单数形式的名称,用于各种消息
'plural' => 'books', // 复数形式的名称,用于表格标题
'ajax' => false // 是否使用 AJAX
) );
}
// ... 更多方法将在后面定义 ...
}
在构造函数中,我们调用了父类的构造函数 parent::__construct()
。这里我们传递一个数组,其中包含一些重要的参数:
singular
: 单数形式的名称,用于各种消息提示,例如“已删除 book”。plural
: 复数形式的名称,用于表格标题,例如“Books”。ajax
: 指定是否使用 AJAX 来加载数据。如果设置为true
,则需要实现ajax_response()
方法。
3. 定义列(get_columns()
)
接下来,我们需要定义列表中的列。这是通过 get_columns()
方法完成的:
function get_columns() {
$columns = array(
'cb' => '<input type="checkbox" />', // 用于批量操作
'title' => 'Title',
'author' => 'Author',
'isbn' => 'ISBN',
'price' => 'Price'
);
return $columns;
}
这个方法返回一个关联数组,键是列的 ID(用于在其他方法中引用),值是列的标题(显示在表格头部)。cb
列是特殊的,用于显示复选框,以便进行批量操作。
4. 可排序的列(get_sortable_columns()
)
如果希望某些列可以排序,我们需要实现 get_sortable_columns()
方法:
function get_sortable_columns() {
$sortable_columns = array(
'title' => array('title', true), // column_id => array(orderby, asc/desc)
'author' => array('author', false), // true means it's already sorted
'price' => array('price', false)
);
return $sortable_columns;
}
这个方法返回一个关联数组,键是列的 ID,值是一个包含两个元素的数组:
orderby
: 用于排序的字段名(通常与列 ID 相同,但也可以不同)。asc/desc
: 一个布尔值,指示列是否默认已排序(true
表示已排序,false
表示未排序)。
5. 准备数据(prepare_items()
)
prepare_items()
方法是 WP_List_Table
中最重要的一个方法。它负责获取数据、进行排序、分页等操作,并将数据传递给表格。
function prepare_items() {
global $wpdb;
$per_page = 5; // 每页显示的记录数
$columns = $this->get_columns();
$hidden = array();
$sortable = $this->get_sortable_columns();
$this->_column_headers = array($columns, $hidden, $sortable);
// 处理排序
$orderby = (!empty($_GET["orderby"])) ? esc_sql($_GET["orderby"]) : 'title';
$order = (!empty($_GET["order"])) ? esc_sql($_GET["order"]) : 'asc';
// 获取总记录数
$total_items = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}my_books");
// 分页
$paged = (!empty($_GET["paged"])) ? esc_sql($_GET["paged"]) : '';
if (empty($paged) || !is_numeric($paged) || $paged <= 0) {
$paged = 1;
}
$total_pages = ceil($total_items / $per_page);
// 获取数据
$offset = ($paged - 1) * $per_page;
$this->items = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}my_books ORDER BY $orderby $order LIMIT %d OFFSET %d", $per_page, $offset), ARRAY_A);
// 配置分页
$this->set_pagination_args(array(
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => $total_pages
));
}
让我们分解一下这个方法:
- 定义变量: 定义了每页显示的记录数
$per_page
。 - 设置列头: 使用
get_columns()
,get_hidden_columns()
(未实现,默认为空数组) 和get_sortable_columns()
方法获取列的定义,并将它们存储在$this->_column_headers
属性中。这是WP_List_Table
类用来呈现表格头部的必要步骤。 - 处理排序: 从
$_GET
变量中获取排序字段和排序方式。esc_sql()
用于防止 SQL 注入。如果$_GET
中没有排序信息,则设置默认排序字段为title
,排序方式为asc
。 - 获取总记录数: 使用
$wpdb
对象查询数据库,获取总记录数。 - 分页: 从
$_GET
变量中获取当前页码。如果$_GET
中没有页码信息,或者页码无效,则设置当前页码为 1。计算总页数。 - 获取数据: 使用
$wpdb
对象查询数据库,获取当前页的数据。$wpdb->prepare()
用于防止 SQL 注入。 - 配置分页: 使用
set_pagination_args()
方法配置分页参数。total_items
是总记录数,per_page
是每页显示的记录数,total_pages
是总页数。
6. 显示数据(column_default()
和自定义列方法)
column_default()
方法用于显示没有自定义处理的列的数据:
function column_default( $item, $column_name ) {
switch( $column_name ) {
case 'isbn':
case 'price':
return $item[ $column_name ];
default:
return '<i>' . $column_name . '</i>'; // 显示列 ID,用于调试
}
}
如果需要自定义显示某些列的数据,可以定义以 column_
开头的方法,例如 column_title()
:
function column_title( $item ) {
$title = '<strong>' . $item['title'] . '</strong>';
$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 $title . $this->row_actions($actions);
}
在这个方法中,我们自定义显示了 title
列的数据,并添加了编辑和删除操作链接。$this->row_actions()
方法用于生成操作链接。
7. 复选框列(column_cb()
)
为了支持批量操作,我们需要实现 column_cb()
方法来显示复选框:
function column_cb( $item ) {
return sprintf(
'<input type="checkbox" name="book[]" value="%s" />', $item['id']
);
}
这个方法返回一个复选框,其值为当前行的 ID。
8. 批量操作(get_bulk_actions()
和 process_bulk_action()
)
为了支持批量操作,我们需要实现 get_bulk_actions()
方法来定义批量操作的选项:
function get_bulk_actions() {
$actions = array(
'delete' => 'Delete'
);
return $actions;
}
这个方法返回一个关联数组,键是操作的 ID,值是操作的名称(显示在下拉菜单中)。
我们还需要实现一个函数来处理批量操作,例如 process_bulk_action()
。这个函数需要在 prepare_items()
方法之前调用。
function process_bulk_action() {
if ( 'delete' === $this->current_action() ) {
$ids = isset($_REQUEST['book']) ? $_REQUEST['book'] : array();
if ( is_array($ids) ) {
$ids = array_map( 'absint', $ids );
}
if ( ! empty( $ids ) ) {
global $wpdb;
$ids_string = implode( ',', $ids );
$wpdb->query( "DELETE FROM {$wpdb->prefix}my_books WHERE id IN($ids_string)" );
}
}
}
9. 显示列表(display()
)
最后,我们需要在 WordPress 后台页面中显示列表。首先,创建一个函数来实例化并显示列表:
function my_custom_list_table_page() {
?>
<div class="wrap">
<h2>My Books</h2>
<form method="post">
<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
<?php
$myListTable = new My_Custom_List_Table();
$myListTable->process_bulk_action(); // 处理批量操作
$myListTable->prepare_items();
$myListTable->display();
?>
</form>
</div>
<?php
}
然后,将这个函数添加到 WordPress 后台菜单:
add_action('admin_menu', 'my_custom_menu');
function my_custom_menu() {
add_menu_page(
'My Books',
'My Books',
'manage_options',
'my_books',
'my_custom_list_table_page'
);
}
完整的示例代码
下面是一个完整的示例代码,展示了如何使用 WP_List_Table
类构建一个简单的后台数据列表:
<?php
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
class My_Custom_List_Table extends WP_List_Table {
function __construct() {
global $status, $page;
parent::__construct( array(
'singular' => 'book', // 单数形式的名称,用于各种消息
'plural' => 'books', // 复数形式的名称,用于表格标题
'ajax' => false // 是否使用 AJAX
) );
}
function get_columns() {
$columns = array(
'cb' => '<input type="checkbox" />', // 用于批量操作
'title' => 'Title',
'author' => 'Author',
'isbn' => 'ISBN',
'price' => 'Price'
);
return $columns;
}
function get_sortable_columns() {
$sortable_columns = array(
'title' => array('title', true), // column_id => array(orderby, asc/desc)
'author' => array('author', false), // true means it's already sorted
'price' => array('price', false)
);
return $sortable_columns;
}
function column_default( $item, $column_name ) {
switch( $column_name ) {
case 'isbn':
case 'price':
return $item[ $column_name ];
default:
return '<i>' . $column_name . '</i>'; // 显示列 ID,用于调试
}
}
function column_title( $item ) {
$title = '<strong>' . $item['title'] . '</strong>';
$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 $title . $this->row_actions($actions);
}
function column_cb( $item ) {
return sprintf(
'<input type="checkbox" name="book[]" value="%s" />', $item['id']
);
}
function get_bulk_actions() {
$actions = array(
'delete' => 'Delete'
);
return $actions;
}
function process_bulk_action() {
if ( 'delete' === $this->current_action() ) {
$ids = isset($_REQUEST['book']) ? $_REQUEST['book'] : array();
if ( is_array($ids) ) {
$ids = array_map( 'absint', $ids );
}
if ( ! empty( $ids ) ) {
global $wpdb;
$ids_string = implode( ',', $ids );
$wpdb->query( "DELETE FROM {$wpdb->prefix}my_books WHERE id IN($ids_string)" );
}
}
}
function prepare_items() {
global $wpdb;
$per_page = 5; // 每页显示的记录数
$columns = $this->get_columns();
$hidden = array();
$sortable = $this->get_sortable_columns();
$this->_column_headers = array($columns, $hidden, $sortable);
// 处理批量操作
$this->process_bulk_action();
// 处理排序
$orderby = (!empty($_GET["orderby"])) ? esc_sql($_GET["orderby"]) : 'title';
$order = (!empty($_GET["order"])) ? esc_sql($_GET["order"]) : 'asc';
// 获取总记录数
$total_items = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}my_books");
// 分页
$paged = (!empty($_GET["paged"])) ? esc_sql($_GET["paged"]) : '';
if (empty($paged) || !is_numeric($paged) || $paged <= 0) {
$paged = 1;
}
$total_pages = ceil($total_items / $per_page);
// 获取数据
$offset = ($paged - 1) * $per_page;
$this->items = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}my_books ORDER BY $orderby $order LIMIT %d OFFSET %d", $per_page, $offset), ARRAY_A);
// 配置分页
$this->set_pagination_args(array(
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => $total_pages
));
}
}
function my_custom_list_table_page() {
?>
<div class="wrap">
<h2>My Books</h2>
<form method="post">
<input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
<?php
$myListTable = new My_Custom_List_Table();
$myListTable->prepare_items();
$myListTable->display();
?>
</form>
</div>
<?php
}
add_action('admin_menu', 'my_custom_menu');
function my_custom_menu() {
add_menu_page(
'My Books',
'My Books',
'manage_options',
'my_books',
'my_custom_list_table_page'
);
}
// 示例数据,用于测试
add_action( 'admin_init', 'my_plugin_create_db' );
function my_plugin_create_db() {
global $wpdb;
$table_name = $wpdb->prefix . 'my_books';
if ( $wpdb->get_var("show tables like '$table_name'") != $table_name ) {
$sql = "CREATE TABLE " . $table_name . " (
id mediumint(9) NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
author varchar(255) NOT NULL,
isbn varchar(20) NOT NULL,
price decimal(10,2) NOT NULL,
UNIQUE KEY id (id)
);";
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sql );
// 插入一些示例数据
$wpdb->insert(
$table_name,
array(
'title' => 'The Lord of the Rings',
'author' => 'J.R.R. Tolkien',
'isbn' => '978-0618260221',
'price' => 25.00,
)
);
$wpdb->insert(
$table_name,
array(
'title' => 'The Hobbit',
'author' => 'J.R.R. Tolkien',
'isbn' => '978-0547928227',
'price' => 18.00,
)
);
$wpdb->insert(
$table_name,
array(
'title' => 'Pride and Prejudice',
'author' => 'Jane Austen',
'isbn' => '978-0141439518',
'price' => 12.00,
)
);
$wpdb->insert(
$table_name,
array(
'title' => '1984',
'author' => 'George Orwell',
'isbn' => '978-0451524935',
'price' => 15.00,
)
);
$wpdb->insert(
$table_name,
array(
'title' => 'To Kill a Mockingbird',
'author' => 'Harper Lee',
'isbn' => '978-0446310789',
'price' => 20.00,
)
);
$wpdb->insert(
$table_name,
array(
'title' => 'The Catcher in the Rye',
'author' => 'J.D. Salinger',
'isbn' => '978-0316769532',
'price' => 16.00,
)
);
}
}
?>
总结
本文介绍了如何使用WP_List_Table
类在WordPress后台构建复杂的列表页面。我们需要继承WP_List_Table
类,实现必要的方法,如get_columns()
、prepare_items()
和column_default()
,以定义列、准备数据并显示数据。掌握这些知识,可以更灵活地定制WordPress后台界面。