I'm using extended WP_List_Table class to output my custom DB data. Facing with a problem - if I search or filter my data, everything works fine, but if I go to the next page or click any sorted header, my data will reset. This happened, because paging and sorting not included search string or filter params ($_REQUEST['s']
) and I don't understand, how to include it. If I switch my global table <form>
method from POST to GET, it will solve the problem, but because action is empty, whole query string including to action and after 3-4 iteration I will get 414 error (uri too large).
<?php
if ( !class_exists( 'WP_List_Table' ) ) {
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
class BWCU_List_Table extends WP_List_Table {
private $_user_fields_mapping = [];
function __construct( $user_fields_mapping ) {
$this->_user_fields_mapping = $user_fields_mapping;
parent::__construct(
[
'singular' => 'blacklist',
'plural' => 'blacklists',
'ajax' => false,
]
);
add_screen_option(
'per_page',
[
'label' => __( 'Items per page', 'textdomain' ),
'default' => 10,
'option' => 'per_page',
]
);
}
function get_columns() {
$columns = [
'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
'value' => __('Name', 'textdomain'),
'type' => __('Type', 'textdomain'),
'reason' => __('Reason', 'textdomain'),
'user_id' => __('User', 'textdomain'),
'order_id' => __('Order', 'textdomain'),
'date' => __('Date', 'textdomain'),
];
return $columns;
}
function column_default( $item, $column_name ) {
return $item[ $column_name ];
}
function column_cb( $item ) {
return sprintf(
'<input type="checkbox" name="id[]" value="%s" />',
$item['ID']
);
}
function column_value( $item ) {
return $item['value'];
}
function column_type( $item ) {
return $this->_user_fields_mapping[ $item['type'] ]['name'];
}
function column_reason( $item ) {
return $item['reason'];
}
function column_user( $item ) {
$user_data = get_userdata( $item['user_id'] );
return !empty( $item['user_id'] ) ? sprintf('<a href="%s" target="_blank">%s</a>', get_edit_user_link( $item['user_id'] ), $user_data->user_login ) : '—';
}
function column_order( $item ) {
return !empty( $item['order_id'] ) ? sprintf('<a href="/wp-admin/post.php?post=%d&action=edit" target="_blank">%s%d</a>', $item['order_id'] , __('Order ', 'textdomain' ), $item['order_id'] ) : '—';
}
function column_date( $item ) {
return $item['created_at'];
}
function process_bulk_action() {
global $wpdb;
if ( 'delete' === $this->current_action() ) {
$ids = isset( $_REQUEST['id'] ) ? $_REQUEST['id'] : [];
if ( !empty( $ids ) ) {
$placeholders = array_fill(0, count( $ids ), '%d');
$wpdb->query(
$wpdb->prepare(
'DELETE FROM ' . $wpdb->prefix . 'bl_data WHERE id IN(' . implode(', ', $placeholders ) . ')',
$ids
)
);
}
}
}
function prepare_items() {
global $wpdb;
$per_page = get_user_meta( get_current_user_id(), get_current_screen()->get_option( 'per_page', 'option' ), true ) ? : 10;
$columns = $this->get_columns();
$hidden = [];
$sortable = $this->get_sortable_columns();
// here we configure table headers, defined in our methods
$this->_column_headers = [ $columns, $hidden, $sortable ];
// [OPTIONAL] process bulk action if any
$this->process_bulk_action();
// prepare query params, as usual current page, order by and order direction
$paged = isset($_REQUEST['paged']) ? max(0, intval($_REQUEST['paged'] - 1) * $per_page) : 0;
$orderby = (isset($_REQUEST['orderby']) && in_array($_REQUEST['orderby'], array_keys($this->get_sortable_columns()))) ? $_REQUEST['orderby'] : 'value';
$order = (isset($_REQUEST['order']) && in_array($_REQUEST['order'], array('asc', 'desc'))) ? $_REQUEST['order'] : 'asc';
$where = $args = [];
if ( isset( $_REQUEST['s'] ) && !empty( $_REQUEST['s'] ) ) {
$where[] = 'value LIKE "%%%s%%"';
$args[] = $_REQUEST['s'];
}
if ( isset( $_REQUEST['filter-type'] ) && !empty( $_REQUEST['filter-type'] ) && ( $_REQUEST['filter-type'] != '0' ) ) {
$where[] = 'type="%s"';
$args[] = $_REQUEST['filter-type'];
}
$where = !empty( $where ) ? ' WHERE ' . implode(' AND ', $where) : '';
$total_query = 'SELECT COUNT(*) FROM ' . $wpdb->prefix . 'bl_data' . $where;
$total_items = $wpdb->get_var( $wpdb->prepare( $total_query, $args ) );
array_push( $args, $per_page, $paged );
$query = 'SELECT ID, value, type, reason, order_id, user_id, created_at FROM ' . $wpdb->prefix . 'bl_data' . $where . ' ORDER BY ' . $orderby . ' ' . $order . ' LIMIT %d OFFSET %d';
$this->items = $wpdb->get_results(
$wpdb->prepare( $query, $args ),
ARRAY_A
);
$this->set_pagination_args(
[
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => ceil( $total_items / $per_page )
]
);
}
function no_items() {
_e( 'No records found.', 'textdomain' );
}
function get_sortable_columns() {
$sortable_columns = [
'value' => ['value', true],
'type' => ['type', false],
'user_id' => ['user_id', false],
'order_id' => ['order_id', false],
'date' => ['date', false],
];
return $sortable_columns;
}
function get_bulk_actions() {
$actions = [
'delete' => __('Delete', 'textdomain')
];
return $actions;
}
function extra_tablenav( $which ) {
if ( $which == "top" ){
?>
<div class="alignleft actions bulkactions">
<select name="filter-type" id="filter-by-type">
<option value="0">All types</option>
<?php foreach ( $this->_user_fields_mapping as $field_id => $field_data ): ?>
<?php $selected = ( $_REQUEST['filter-type'] == $field_id ) ? ' selected' : ''; ?>
<option value="<?php echo $field_id; ?>"<?php echo $selected; ?>><?php echo $field_data['name']; ?></option>
<?php endforeach; ?>
</select>
<input type="submit" class="button">
</div>
<?php
}
}
function search_box( $text, $input_id ) {
if ( empty( $_REQUEST['s'] ) && !$this->has_items() ) {
return;
}
$input_id = $input_id . '-search-input';
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_mime_type'] ) ) {
echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
}
if ( ! empty( $_REQUEST['detached'] ) ) {
echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
}
?>
<p class="search-box">
<label class="screen-reader-text" for="<?php echo $input_id ?>"><?php echo $text; ?>:</label>
<input type="search" id="<?php echo $input_id ?>" name="s" value="<?php _admin_search_query(); ?>" />
<?php submit_button( $text, 'button', false, false, ['id' => 'search-submit'] ); ?>
</p>
<?php
}
}
set_url_scheme
-hook as outlined here: www.joho.se/2021/03/24/keep-search-parameters-when-using-wp_list_table-and-navigation (It's a bit too long to include here I think.)