Add search Value to wp_list_table pagination

I have a custom WordPress table using wp_list_table, with a search field: the search works well, but I am seeing that the search value isn’t added to the pagination links when there are multiple pages of results. Here is my complete code:

if ( ! class_exists( 'WP_List_Table' ) ) {
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}

class Customers_List extends WP_List_Table {

    /** Class constructor */
    public function __construct() {

        parent::__construct( [
            'singular' => __( 'Order', 'sp' ), //singular name of the listed records
            'plural'   => __( 'Orders', 'sp' ), //plural name of the listed records
            'ajax'     => false //does this table support ajax?
        ] );

    }


    /**
     * Retrieve customers data from the database
     *
     * @param int $per_page
     * @param int $page_number
     *
     * @return mixed
     */
    public static function get_customers( $per_page = 5, $page_number = 1 ) {

        global $wpdb;

        $sql = "SELECT * FROM old_order";

        if( ! empty( $_REQUEST['s'] ) ){
                        $search = esc_sql( $_REQUEST['s'] );
            $sql .= " WHERE firstname LIKE '%{$search}%'";
            $sql .= " OR lastname LIKE '%{$search}%'";
            $sql .= " OR order_id = '{$search}'";
        }

        if ( ! empty( $_REQUEST['orderby'] ) ) {
            $sql .= ' ORDER BY ' . esc_sql( $_REQUEST['orderby'] );
            $sql .= ! empty( $_REQUEST['order'] ) ? ' ' . esc_sql( $_REQUEST['order'] ) : ' ASC';
        }

        $sql .= " LIMIT $per_page";
        $sql .= ' OFFSET ' . ( $page_number - 1 ) * $per_page;

        // echo $sql;

        $result = $wpdb->get_results( $sql, 'ARRAY_A' );

        return $result;
    }


    /**
     * Delete a customer record.
     *
     * @param int $id customer ID
     */
    public static function delete_customer( $id ) {
        global $wpdb;

        $wpdb->delete(
            "old_order",
            [ 'ID' => $id ],
            [ '%d' ]
        );
    }


    /**
     * Returns the count of records in the database.
     *
     * @return null|string
     */
    public static function record_count() {
        global $wpdb;

        $sql = "SELECT COUNT(*) FROM old_order";

        if( ! empty( $_REQUEST['s'] ) ){
            $search = esc_sql( $_REQUEST['s'] );
            $sql .= " WHERE firstname LIKE '%{$search}%'";
            $sql .= " OR lastname LIKE '%{$search}%'";
            $sql .= " OR order_id LIKE '%{$search}%'";
        }

        return $wpdb->get_var( $sql );
    }


    /** Text displayed when no customer data is available */
    public function no_items() {
        _e( 'No orders available..', 'sp' );
    }


    /**
     * Render a column when no column specific method exist.
     *
     * @param array $item
     * @param string $column_name
     *
     * @return mixed
     */
    public function column_default( $item, $column_name ) {
        switch ( $column_name ) {
            case 'order_id':
                return $item[ $column_name ];
            case 'firstname':
                return $item[ $column_name ];
            case 'lastname':
                return $item[ $column_name ];
            case 'total':
                return "$" . number_format( $item[ $column_name ] );
            case 'date_added':
                return date('m/d/Y g:i:s A', strtotime( $item[ $column_name ] ) );
            default:
                return print_r( $item, true ); //Show the whole array for troubleshooting purposes
        }
    }

    /**
     * Render the bulk edit checkbox
     *
     * @param array $item
     *
     * @return string
     */
    function column_cb( $item ) {
        return sprintf(
            '<input type="checkbox" name="bulk-delete[]" value="https://wordpress.stackexchange.com/questions/305378/%s" />', $item['ID']
        );
    }


    /**
     * Method for name column
     *
     * @param array $item an array of DB data
     *
     * @return string
     */
    function column_name( $item ) {

        $delete_nonce = wp_create_nonce( 'sp_delete_customer' );

        $title="<strong>" . $item['name'] . '</strong>';

        $actions = [
            'delete' => sprintf( '<a href="https://wordpress.stackexchange.com/questions/305378/?page=%s&action=%s&customer=%s&_wpnonce=%s">Delete</a>', esc_attr( $_REQUEST['page'] ), 'delete', absint( $item['ID'] ), $delete_nonce )
        ];

        return $title . $this->row_actions( $actions );
    }


    /**
     *  Associative array of columns
     *
     * @return array
     */
    function get_columns() {
        $columns = [
            'cb'      => '<input type="checkbox" />',
            'order_id'    => __( 'Order ID', 'sp' ),
            'firstname' => __( 'First Name', 'sp' ),
            'lastname'    => __( 'Last Name', 'sp' ),
            'total'    => __( 'Total', 'sp' ),
            'date_added'    => __( 'Date Added', 'sp' )
        ];

        return $columns;
    }


    /**
     * Columns to make sortable.
     *
     * @return array
     */
    public function get_sortable_columns() {
        $sortable_columns = array(
            'order_id' => array( 'order_id', true ),
            'firstname' => array( 'firstname', true ),
            'lastname' => array( 'lastname', true )
        );

        return $sortable_columns;
    }

    /**
     * Returns an associative array containing the bulk action
     *
     * @return array
     */
    public function get_bulk_actions() {
        $actions = [
            'bulk-delete' => 'Delete'
        ];

        return $actions;
    }


    /**
     * Handles data query and filter, sorting, and pagination.
     */
    public function prepare_items( ) {

        $this->_column_headers = $this->get_column_info();

        /** Process bulk action */
        $this->process_bulk_action();

        $per_page     = $this->get_items_per_page( 'customers_per_page', 20 );
        $current_page = $this->get_pagenum();
        $total_items  = self::record_count();

        $this->set_pagination_args( [
            '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 = self::get_customers( $per_page, $current_page );
    }

    public function process_bulk_action() {

        //Detect when a bulk action is being triggered...
        if ( 'delete' === $this->current_action() ) {

            // In our file that handles the request, verify the nonce.
            $nonce = esc_attr( $_REQUEST['_wpnonce'] );

            if ( ! wp_verify_nonce( $nonce, 'sp_delete_customer' ) ) {
                die( 'Go get a life script kiddies' );
            }
            else {
                self::delete_customer( absint( $_GET['customer'] ) );

                        // esc_url_raw() is used to prevent converting ampersand in url to "#038;"
                        // add_query_arg() return the current url
                        wp_redirect( esc_url_raw(add_query_arg()) );
                exit;
            }

        }

        // If the delete bulk action is triggered
        if ( ( isset( $_POST['action'] ) && $_POST['action'] == 'bulk-delete' )
             || ( isset( $_POST['action2'] ) && $_POST['action2'] == 'bulk-delete' )
        ) {

            $delete_ids = esc_sql( $_POST['bulk-delete'] );

            // loop over the array of record IDs and delete them
            foreach ( $delete_ids as $id ) {
                self::delete_customer( $id );

            }

            // esc_url_raw() is used to prevent converting ampersand in url to "#038;"
                // add_query_arg() return the current url
                wp_redirect( esc_url_raw(add_query_arg()) );
            exit;
        }
    }

}


class SP_Plugin {

    // class instance
    static $instance;

    // customer WP_List_Table object
    public $customers_obj;

    // class constructor
    public function __construct() {
        add_filter( 'set-screen-option', [ __CLASS__, 'set_screen' ], 10, 3 );
        add_action( 'admin_menu', [ $this, 'plugin_menu' ] );
    }


    public static function set_screen( $status, $option, $value ) {
        return $value;
    }

    public function plugin_menu() {

        $hook = add_menu_page(
            'Order Lookup',
            'Order Lookup',
            'manage_options',
            'wp_list_table_class',
            [ $this, 'plugin_settings_page' ]
        );

        add_action( "load-$hook", [ $this, 'screen_option' ] );

    }


    /**
     * Plugin settings page
     */
    public function plugin_settings_page() {
        ?>
        <div class="wrap">
            <h2>Previous Order System Lookup</h2>

            <div id="poststuff">
                <div id="post-body" class="metabox-holder columns-2">
                    <div id="post-body-content">
                        <div class="meta-box-sortables ui-sortable">
                            <form method="get">
                                <?php
                                $this->customers_obj->prepare_items();
                                $this->customers_obj->search_box('Search', 'search');
                                $this->customers_obj->display(); ?>
                            </form>
                        </div>
                    </div>
                </div>
                <br class="clear">
            </div>
        </div>
    <?php
    }

    /**
     * Screen options
     */
    public function screen_option() {

        $option = 'per_page';
        $args   = [
            'label'   => 'Customers',
            'default' => 20,
            'option'  => 'customers_per_page'
        ];

        add_screen_option( $option, $args );

        $this->customers_obj = new Customers_List();
    }


    /** Singleton instance */
    public static function get_instance() {
        if ( ! isset( self::$instance ) ) {
            self::$instance = new self();
        }

        return self::$instance;
    }

}


add_action( 'plugins_loaded', function () {
    SP_Plugin::get_instance();
} );

Everything works as intended, except for adding the search value to the pagination links when there are multiple pages. So when you click on ‘next’ it removed the search value and returns all the results (on the next page). I used this Gist as a reference: https://gist.github.com/paulund/7659452

2 Answers
2

I solved my issue by passing the page variable as a hidden field:

<form method="get">
    <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
    <?php
    $this->customers_obj->prepare_items();
    $this->customers_obj->search_box('Search', 'search');
    $this->customers_obj->display(); ?>
</form>

Leave a Comment