如何利用`WP_List_Table`类构建复杂的后台数据列表,并实现高级筛选和分页?

构建复杂 WordPress 后台数据列表:WP_List_Table 深度解析与实践

大家好,今天我们来深入探讨如何利用 WordPress 的 WP_List_Table 类来构建复杂的后台数据列表,并实现高级筛选和分页功能。WP_List_Table 是一个强大的工具,可以帮助我们轻松地在 WordPress 后台创建自定义的数据展示界面,提供用户友好的交互体验。

1. WP_List_Table 类概述

WP_List_Table 类位于 wp-admin/includes/class-wp-list-table.php 文件中。它是一个抽象类,我们需要继承它并实现其中的一些方法,才能创建我们自己的列表。该类提供了以下核心功能:

  • 数据展示: 将数据以表格的形式展示在后台。
  • 分页: 自动处理分页逻辑,方便用户浏览大量数据。
  • 排序: 允许用户根据不同的列对数据进行排序。
  • 批量操作: 支持用户对选中的数据进行批量操作。
  • 列管理: 允许用户自定义显示的列。
  • 搜索: 提供搜索功能,方便用户查找数据。

2. 创建自定义列表类

首先,我们需要创建一个自定义类,继承 WP_List_Table。例如,我们创建一个 My_Custom_List_Table 类来展示一些自定义数据,比如用户提交的反馈信息。

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 {

    public function __construct() {
        parent::__construct( [
            'singular' => 'feedback',     // 单数形式,用于 URL 参数
            'plural'   => 'feedbacks',   // 复数形式,用于 URL 参数
            'ajax'     => false        // 是否启用 AJAX 分页和排序
        ] );
    }

    // ... 其他方法将在后面实现
}

在构造函数中,我们调用了父类的构造函数,并传入了一个数组作为参数。这个数组定义了一些基本属性,例如单数和复数形式的名称,以及是否启用 AJAX。

3. 定义列

接下来,我们需要定义要在表格中显示的列。我们需要实现 get_columns() 方法,该方法返回一个关联数组,键是列的 ID,值是列的标题。

    public function get_columns() {
        $columns = [
            'cb'      => '<input type="checkbox" />', // 用于批量操作的复选框
            'id'       => 'ID',
            'name'    => '姓名',
            'email'   => '邮箱',
            'message' => '留言',
            'date'    => '日期'
        ];
        return $columns;
    }

cb 列是用于批量操作的复选框列,它是必须的。

4. 定义可排序的列

如果我们希望用户能够根据某些列对数据进行排序,我们需要实现 get_sortable_columns() 方法。该方法返回一个关联数组,键是列的 ID,值是一个数组,包含列的 ID 和一个布尔值,表示是否默认升序排序。

    public function get_sortable_columns() {
        $sortable_columns = [
            'id'    => [ 'id', true ],  // ID 列可排序,默认升序
            'name'  => [ 'name', false ], // 姓名列可排序,默认降序
            'email' => [ 'email', false ], // 邮箱列可排序,默认降序
            'date'  => [ 'date', true ]   // 日期列可排序,默认升序
        ];
        return $sortable_columns;
    }

5. 准备数据

在展示数据之前,我们需要从数据库或其他来源获取数据,并将其格式化为 WP_List_Table 类可以使用的格式。我们需要实现 prepare_items() 方法。

    public function prepare_items() {
        global $wpdb;

        $per_page = 20; // 每页显示的数据条数

        $columns = $this->get_columns();
        $hidden = [];
        $sortable = $this->get_sortable_columns();

        $this->_column_headers = [ $columns, $hidden, $sortable ];

        $current_page = $this->get_pagenum();

        // 获取排序参数
        $orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : 'date';
        $order = isset( $_GET['order'] ) ? $_GET['order'] : 'desc';

        // 构建 SQL 查询
        $table_name = $wpdb->prefix . 'my_custom_table'; // 替换为你的表名
        $sql = "SELECT * FROM $table_name";

        // 处理排序
        if ( ! empty( $orderby ) ) {
            $sql .= ' ORDER BY ' . esc_sql( $orderby );
            $sql .= ! empty( $order ) ? ' ' . esc_sql( $order ) : ' ASC';
        }

        // 计算总数据条数
        $total_items = $wpdb->query( $sql );

        // 处理分页
        $sql .= " LIMIT " . ( ( $current_page - 1 ) * $per_page ) . " , " . $per_page;

        // 获取数据
        $data = $wpdb->get_results( $sql, ARRAY_A ); // ARRAY_A 返回关联数组

        $this->items = $data;

        // 配置分页
        $this->set_pagination_args( [
            'total_items' => $total_items, // 总数据条数
            'per_page'    => $per_page,       // 每页显示的数据条数
            'total_pages' => ceil( $total_items / $per_page )   // 总页数
        ] );
    }

在这个方法中,我们首先定义了每页显示的数据条数。然后,我们获取了列的定义、隐藏列和可排序的列。接下来,我们构建了 SQL 查询,从数据库中获取数据。我们还处理了排序和分页逻辑。最后,我们将获取到的数据赋值给 $this->items 属性,并配置了分页参数。

6. 展示数据

现在,我们需要实现 column_default() 方法,该方法用于展示默认列的数据。对于 cb 列,我们需要实现 column_cb() 方法。

    public function column_default( $item, $column_name ) {
        switch ( $column_name ) {
            case 'id':
                return $item['id'];
            case 'name':
                return $item['name'];
            case 'email':
                return $item['email'];
            case 'message':
                return substr( $item['message'], 0, 50 ) . '...'; // 截取部分内容
            case 'date':
                return date( 'Y-m-d H:i:s', strtotime( $item['date'] ) );
            default:
                return print_r( $item, true ); // 用于调试,显示原始数据
        }
    }

    public function column_cb( $item ) {
        return sprintf(
            '<input type="checkbox" name="bulk-delete[]" value="%s" />', $item['id']
        );
    }

column_default() 方法中,我们根据列的 ID,从数据中获取相应的值,并将其展示出来。在 column_cb() 方法中,我们生成了用于批量操作的复选框。

7. 批量操作

为了实现批量操作,我们需要实现 get_bulk_actions() 方法,该方法返回一个关联数组,键是操作的 ID,值是操作的标题。

    public function get_bulk_actions() {
        $actions = [
            'delete' => '删除'
        ];
        return $actions;
    }

然后,我们需要在 prepare_items() 方法中处理批量操作的逻辑。

    public function prepare_items() {
        global $wpdb;

        // ... 前面的代码省略

        $this->process_bulk_action(); // 处理批量操作

        // ... 后面的代码省略
    }

    private function process_bulk_action() {
        if ( 'delete' === $this->current_action() ) {
            $ids = isset( $_POST['bulk-delete'] ) ? $_POST['bulk-delete'] : [];
            if ( ! empty( $ids ) ) {
                $ids = array_map( 'absint', $ids ); // 转换为整数
                $ids_string = implode( ',', $ids );

                global $wpdb;
                $table_name = $wpdb->prefix . 'my_custom_table'; // 替换为你的表名
                $wpdb->query(
                    "DELETE FROM $table_name WHERE id IN ($ids_string)"
                );

                echo '<div class="updated"><p>已删除 ' . count( $ids ) . ' 条数据。</p></div>';
            }
        }
    }

process_bulk_action() 方法中,我们首先判断当前的操作是否是删除操作。如果是,我们获取选中的数据的 ID,然后从数据库中删除这些数据。

8. 添加搜索功能

为了添加搜索功能,我们需要重写 prepare_items 方法,在 SQL 查询中加入 WHERE 子句。

    public function prepare_items() {
        global $wpdb;

        $per_page = 20; // 每页显示的数据条数

        $columns = $this->get_columns();
        $hidden = [];
        $sortable = $this->get_sortable_columns();

        $this->_column_headers = [ $columns, $hidden, $sortable ];

        $this->process_bulk_action(); // 处理批量操作

        $current_page = $this->get_pagenum();

        // 获取排序参数
        $orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : 'date';
        $order = isset( $_GET['order'] ) ? $_GET['order'] : 'desc';

        // 获取搜索词
        $search_term = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : '';

        // 构建 SQL 查询
        $table_name = $wpdb->prefix . 'my_custom_table'; // 替换为你的表名
        $sql = "SELECT * FROM $table_name";

        // 处理搜索
        if ( ! empty( $search_term ) ) {
            $search_term = esc_sql( $search_term );
            $sql .= " WHERE name LIKE '%$search_term%' OR email LIKE '%$search_term%' OR message LIKE '%$search_term%'"; // 可以根据需要添加更多的搜索字段
        }

        // 处理排序
        if ( ! empty( $orderby ) ) {
            $sql .= ' ORDER BY ' . esc_sql( $orderby );
            $sql .= ! empty( $order ) ? ' ' . esc_sql( $order ) : ' ASC';
        }

        // 计算总数据条数
        $total_items = $wpdb->query( $sql );

        // 处理分页
        $sql .= " LIMIT " . ( ( $current_page - 1 ) * $per_page ) . " , " . $per_page;

        // 获取数据
        $data = $wpdb->get_results( $sql, ARRAY_A ); // ARRAY_A 返回关联数组

        $this->items = $data;

        // 配置分页
        $this->set_pagination_args( [
            'total_items' => $total_items, // 总数据条数
            'per_page'    => $per_page,       // 每页显示的数据条数
            'total_pages' => ceil( $total_items / $per_page )   // 总页数
        ] );
    }

    public function search_box( $text, $input_id ) {
        if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
            return;
        }

        ?>
        <form id="feedback-filter" method="get">
            <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
            <?php $this->search_box_input( $input_id ); ?>
            <?php submit_button( $text, 'button', false, false, array('id' => 'search-submit') ); ?>
        </form>
        <?php
    }

    private function search_box_input( $input_id ) {
        if ( ! empty( $_REQUEST['orderby'] ) ) {
            echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
        }
        if ( ! empty( $_REQUEST['order'] ) ) {
            echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
        }
        if ( ! empty( $_REQUEST['post_status'] ) ) {
            echo '<input type="hidden" name="post_status" value="' . esc_attr( $_REQUEST['post_status'] ) . '" />';
        }
        ?>
        <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_attr( '搜索反馈:' ); ?></label>
        <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
        <?php
    }

我们从 $_REQUEST['s'] 中获取搜索词,并将其添加到 SQL 查询的 WHERE 子句中。我们还添加了一个搜索框,方便用户输入搜索词。

9. 添加高级筛选

高级筛选允许用户根据更复杂的条件筛选数据。我们可以通过添加自定义的表单元素来实现高级筛选。

    public function extra_tablenav( $which ) {
        if ( $which == "top" ) {
            // 在表格上方添加自定义筛选表单
            ?>
            <div class="alignleft actions">
                <select name="filter_status">
                    <option value="">所有状态</option>
                    <option value="pending" <?php selected( isset( $_GET['filter_status'] ) ? $_GET['filter_status'] : '', 'pending' ); ?>>待处理</option>
                    <option value="approved" <?php selected( isset( $_GET['filter_status'] ) ? $_GET['filter_status'] : '', 'approved' ); ?>>已批准</option>
                    <option value="rejected" <?php selected( isset( $_GET['filter_status'] ) ? $_GET['filter_status'] : '', 'rejected' ); ?>>已拒绝</option>
                </select>
                <?php
                submit_button( '筛选', 'button', 'filter_action', false, array( 'id' => 'filter-submit' ) );
                ?>
            </div>
            <?php
        }
        if ( $which == "bottom" ) {
            // 在表格下方添加其他内容(可选)
        }
    }

    public function prepare_items() {
        global $wpdb;

        // ... 前面的代码省略

        // 获取筛选参数
        $filter_status = isset( $_GET['filter_status'] ) ? $_GET['filter_status'] : '';

        // 构建 SQL 查询
        $table_name = $wpdb->prefix . 'my_custom_table'; // 替换为你的表名
        $sql = "SELECT * FROM $table_name";

        // 处理筛选
        if ( ! empty( $filter_status ) ) {
            $filter_status = esc_sql( $filter_status );
            $sql .= " WHERE status = '$filter_status'";
        }

        // ... 后面的代码省略
    }

我们使用 extra_tablenav() 方法在表格上方添加了一个自定义的筛选表单,包含一个状态选择框。然后,我们在 prepare_items() 方法中获取筛选参数,并将其添加到 SQL 查询的 WHERE 子句中。

10. 在后台页面中使用列表类

最后,我们需要在后台页面中使用我们创建的列表类。

function my_custom_admin_page() {
    ?>
    <div class="wrap">
        <h1>用户反馈</h1>

        <?php
        $my_list_table = new My_Custom_List_Table();
        $my_list_table->prepare_items();

        // 添加搜索框
        ?>
        <form method="post">
            <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
            <?php
            $my_list_table->search_box( '搜索', 'feedbacksearch' );
            ?>
        </form>
        <?php

        $my_list_table->display();
        ?>
    </div>
    <?php
}

function my_custom_admin_menu() {
    add_menu_page(
        '用户反馈',
        '用户反馈',
        'manage_options',
        'my-custom-admin-page',
        'my_custom_admin_page'
    );
}
add_action( 'admin_menu', 'my_custom_admin_menu' );

我们创建了一个后台页面,并在该页面中实例化了 My_Custom_List_Table 类。然后,我们调用 prepare_items() 方法准备数据,最后调用 display() 方法展示列表。

总结: 构建强大的后台数据列表

通过继承 WP_List_Table 类,定义列、排序、批量操作、搜索和高级筛选,我们可以轻松构建强大的后台数据列表,为用户提供便捷的数据管理界面。

最后要说的话

WP_List_Table 类功能强大,用法灵活,需要根据具体需求进行调整和扩展。希望这篇文章能够帮助你更好地理解和使用 WP_List_Table 类,构建出更加优秀的 WordPress 后台界面。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注