¿Cómo crear opciones de filtro personalizadas en wp_list_table?
Usé la clase wp_list_table para crear mi tabla personalizada en el backend. Está funcionando bien.
Ahora, quiero agregar filtros como en la imagen de abajo.

Aquí está mi código existente para renderizar la tabla de administración con mi información personalizada.
if( ! class_exists( 'WP_List_Table' ) ) {
    require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
class Kv_subscribers_list extends WP_List_Table {
    function __construct(){
        global $status, $page;                
        parent::__construct( array(
            'singular'  => 'notificación',  // Nombre singular
            'plural'    => 'notificaciones',   // Nombre plural
            'ajax'      => false      
        ) );        
    }
    function column_default($item, $column_name){
        switch($column_name){
            case 'email':
            case 'date':
            case 'common':          
            case 'unit_id':          
                return $item[$column_name];
            default:
                return print_r($item,true); // Muestra todo el array para propósitos de depuración
        }
    }
    function column_email($item){       
        $actions = array(
            'email'     => sprintf('<a href="?page=%s&action=%s&email_id=%s">Correo</a>',$_REQUEST['page'],'email',$item['id']),
            'delete'    => sprintf('<a href="?page=%s&action=%s&delete_id=%s">Eliminar</a>',$_REQUEST['page'],'delete',$item['id']),
        );
        // Devuelve el contenido del título
        return sprintf('%1$s %2$s',
            /*$1%s*/ $item['email'],          
            /*$2%s*/ $this->row_actions($actions)
        );
    }
    function column_cb($item){
        return sprintf(
            '<input type="checkbox" name="%1$s[]" value="%2$s" />',
            /*$1%s*/ $this->_args['singular'],  
            /*$2%s*/ $item['id']                
        );
    }
    function get_columns(){
        $columns = array(
            'cb'        => '<input type="checkbox" />', // Renderiza un checkbox en lugar de texto
            'email'=>__('Fecha'),  
            'date'=>__('Fecha'),  
            'common'=>__('Alerta Común'),           
            'unit_id'=>__('ID Único')
        );
        return $columns;
    }
   public function get_sortable_columns() {
        $sortable_columns = array(
            'email'   => array('wp_user_id',false),     // true significa que ya está ordenado
            'date'    => array('date',false),
            'common'  => array('common',false)
        );
        return $sortable_columns;
    }
   public function get_bulk_actions() {
        $actions = array(
            'delete'    => 'Eliminar',
            'email'     => 'Correo'
        );
        return $actions;
    }
   public function process_bulk_action() {
        global $wpdb; 
        $notifications_tbl = $wpdb->prefix.'newsletter';
        if( 'delete'===$this->current_action() ) {
            foreach($_POST['notification'] as $single_val){
                $wpdb->delete( $notifications_tbl, array( 'id' => (int)$single_val ) );             
            }
            $redirect_url =  get_admin_url( null, 'admin.php?page=subscribers' );
            wp_safe_redirect($redirect_url); 
            wp_die('¡Elementos eliminados (o lo serían si tuviéramos elementos para eliminar)!');
        } 
        if( 'email'===$this->current_action() ) {           
                $result_email_ar = implode("-",$_POST['notification']);
            $redirect_url =  get_admin_url( null, 'admin.php?page=kvcodes&ids='.$result_email_ar  );
            wp_safe_redirect($redirect_url);        
            wp_die('  ');
        }       
    }
    function prepare_items() {
        global $wpdb; // Esto se usa solo si se realizan consultas a la base de datos
        $database_name = $wpdb->prefix.'newsletter' ;
        $per_page = 10;
        $query = "SELECT * FROM $database_name ORDER BY id DESC";
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = $this->get_sortable_columns();
        $this->_column_headers = array($columns, $hidden, $sortable);        
        $this->process_bulk_action();
        $data =  $wpdb->get_results($query, ARRAY_A );
        function usort_reorder($a,$b){
            $orderby = (!empty($_REQUEST['orderby'])) ? $_REQUEST['orderby'] : 'title'; // Si no hay orden, por defecto título
            $order = (!empty($_REQUEST['order'])) ? $_REQUEST['order'] : 'asc'; // Si no hay orden, por defecto ascendente
            $result = strcmp($a[$orderby], $b[$orderby]); // Determina el orden
            return ($order==='asc') ? $result : -$result; // Envía la dirección de ordenamiento final a usort
        }
       // usort($data, 'usort_reorder');        
        $current_page = $this->get_pagenum();        
        $total_items = count($data);
        $data = array_slice($data,(($current_page-1)*$per_page),$per_page); 
        $this->items = $data;
        $this->set_pagination_args( array(
            'total_items' => $total_items,                  // Tenemos que calcular el número total de elementos
            'per_page'    => $per_page,                     // Tenemos que determinar cuántos elementos mostrar por página
            'total_pages' => ceil($total_items/$per_page)   // Tenemos que calcular el número total de páginas
        ) );
    }
}//classy
echo '<form method="post">';
    $mydownloads = new Kv_subscribers_list(); 
    echo '</pre><div class="wrap"><h2>Suscriptores<a href="'."http://".$_SERVER["SERVER_NAME"].$_SERVER['REQUEST_URI'].'&add_new=true" class="add-new-h2">Añadir Nuevo</a></h2>'; 
    $mydownloads->prepare_items(); 
    $mydownloads->display(); 
    echo '</div></form>'; 
Ahora, quiero hacer los filtros como
Todos || Publicados || Papelera
Necesito crear un filtro personalizado como este.
 
                            Código
class Kv_subscribers_list extends WP_List_Table {
function __construct(){
    global $status, $page;                
    parent::__construct( array(
        'singular'  => 'notification',  
        'plural'    => 'notifications',   
        'ajax'      => false      
    ) );        
}
protected function get_views() { 
    $status_links = array(
        "all"       => __("<a href='#'>All</a>",'my-plugin-slug'),
        "published" => __("<a href='#'>Published</a>",'my-plugin-slug'),
        "trashed"   => __("<a href='#'>Trashed</a>",'my-plugin-slug')
    );
    return $status_links;
}
function column_default($item, $column_name){
    switch($column_name){
        case 'email':
        case 'date':
        case 'common':          
        case 'unit_id':          
            return $item[$column_name];
        default:
            return print_r($item,true);
    }
}
function column_email($item){       
    $actions = array(
        'email'     => sprintf('<a href="?page=%s&action=%s&email_id=%s">E-mail</a>',$_REQUEST['page'],'email',$item['id']),
        'delete'    => sprintf('<a href="?page=%s&action=%s&delete_id=%s">Delete</a>',$_REQUEST['page'],'delete',$item['id']),
    );
    return sprintf('%1$s %2$s',
        /*$1%s*/ $item['email'],          
        /*$2%s*/ $this->row_actions($actions)
    );
}
function column_cb($item){
    return sprintf(
        '<input type="checkbox" name="%1$s[]" value="%2$s" />',
        /*$1%s*/ $this->_args['singular'],  
        /*$2%s*/ $item['id']                
    );
}
function get_columns(){
    $columns = array(
        'cb'        => '<input type="checkbox" />', 
        'email'=>__('Email'),  
        'date'=>__('Date'),  
        'common'=>__('Common Alert'),           
        'unit_id'=>__('Unique ID')
    );
    return $columns;
}
public function get_sortable_columns() {
    $sortable_columns = array(
        'email'   => array('wp_user_id',false),
        'date'    => array('date',false),
        'common'  => array('common',false)
    );
    return $sortable_columns;
}
public function get_bulk_actions() {
    $actions = array(
        'delete'    => 'Delete',
        'email'     => 'Email'
    );
    return $actions;
}
public function process_bulk_action() {
    global $wpdb; 
    $notifications_tbl = $wpdb->prefix.'newsletter';
    if( 'delete'===$this->current_action() ) {
        foreach($_POST['notification'] as $single_val){
            $wpdb->delete( $notifications_tbl, array( 'id' => (int)$single_val ) );             
        }
        $redirect_url = get_admin_url( null, 'admin.php?page=subscribers' );
        wp_safe_redirect($redirect_url); 
        wp_die('Items deleted (or they would be if we had items to delete)!');
    } 
    if( 'email'===$this->current_action() ) {           
            $result_email_ar = implode("-",$_POST['notification']);
        $redirect_url = get_admin_url( null, 'admin.php?page=kvcodes&ids='.$result_email_ar );
        wp_safe_redirect($redirect_url);        
        wp_die('  ');
    }       
}
function prepare_items() {
    global $wpdb;
    $database_name = $wpdb->prefix.'newsletter';
    $per_page = 10;
    $query = "SELECT * FROM $database_name ORDER BY id DESC";
    $columns = $this->get_columns();
    $hidden = array();
    $sortable = $this->get_sortable_columns();
    $this->_column_headers = array($columns, $hidden, $sortable);        
    $this->process_bulk_action();
    $data = $wpdb->get_results($query, ARRAY_A);
    function usort_reorder($a,$b){
        $orderby = (!empty($_REQUEST['orderby'])) ? $_REQUEST['orderby'] : 'title';
        $order = (!empty($_REQUEST['order'])) ? $_REQUEST['order'] : 'asc';
        $result = strcmp($a[$orderby], $b[$orderby]);
        return ($order==='asc') ? $result : -$result;
    }
    $current_page = $this->get_pagenum();        
    $total_items = count($data);
    $data = array_slice($data,(($current_page-1)*$per_page),$per_page); 
    $this->items = $data;
    $this->set_pagination_args( array(
        'total_items' => $total_items,
        'per_page'    => $per_page,
        'total_pages' => ceil($total_items/$per_page)
    ) );
}
}//class
Página de Administración & Ejemplo de Renderizado
function o_add_menu_items(){
    add_menu_page('Plugin List Table', 'Sub', 'activate_plugins', 'subscribers', 'o_render_list_page');
} 
add_action('admin_menu', 'o_add_menu_items');
function o_render_list_page() {
    $mydownloads = new Kv_subscribers_list();
    $title = __("Subscribers","my_plugin_slug");
    ?>
    <div class="wrap">
        <h1>
            <?php echo esc_html( $title );?>
             <a href="<?php echo admin_url( 'admin.php?page=subscribers&add_new=true' ); ?>" class="page-title-action">
                <?php echo esc_html_x('Add New', 'my-plugin-slug'); ?>
            </a>
            <?php
            ?>
        </h1>
    </div>
    <?php $mydownloads->views(); ?>
    <form method="post">
    <?php 
        $mydownloads->prepare_items(); 
        $mydownloads->display();
    ?>
    </form>
<?php
}
Explicación
We need to override WP_List_Table class method get_views to get status links on the top.By default it is an empty array.
views method of WP_List_Table uses get_views to display list of those links that we return in the get_views as an associative array with separator |.
We can also override views method to have more control , for example if we want to change separator.
Take a look at how other list tables are using that. For instance check WP_Posts_List_Table.
After you have overridden the method then place $mydownloads->views(); .be sure to escape all and internationalize strings.
Using Filter
We can use views_{$this->screen->id} filter once we have added get_views method.
Assuming toplevel_page_subscribers as screen id
add_filter('views_toplevel_page_subscribers','my_plugin_slug_status_links',10, 1);
function my_plugin_slug_status_links($views) {
   $views['scheduled'] = "<a href='#'>Scheduled</a>";
   return $views;
}
 
                                Bueno, sé que esto llega bastante tarde, pero como este es el primer resultado en Google al buscar sobre filtrado en WP_List_Table, debo decirles que existe la función extra_tablenav disponible para sobrescribir en tu extensión de clase:
class Kv_subscribers_list extends WP_List_Table {
    function extra_tablenav( $which )
    {
        switch ( $which )
        {
            case 'top':
                // Tu código HTML para mostrar
                break;
            case 'bottom':
                // Tu código HTML para mostrar
                break;
        }
    }
}
Si deseas mostrar contenido en ambas secciones, simplemente ignora el switch y coloca tu código HTML directamente al inicio de la función.
 
                                Puedes usar
<?php
class Kv_subscribers_list extends WP_List_Table {
    function extra_tablenav( $which )
    {
        switch ( $which )
        {
            case 'top':
                // Tu código HTML para mostrar
                global $wpdb, $wp_locale;
                    $extra_checks = "AND status != 'auto-draft'";
                    if ( ! isset( $_GET['status'] ) || 'trash' !== $_GET['status'] ) {
                        $extra_checks .= " AND status != 'trash'";
                    } elseif ( isset( $_GET['status'] ) ) {
                        $extra_checks = $wpdb->prepare( ' AND status = %s', $_GET['status'] );
                    }
                    $sql = "
                    SELECT DISTINCT YEAR( create_date ) AS year, MONTH( create_date ) AS month
                    FROM ".$wpdb->prefix."audio_record
                    WHERE 1 = 1
                    $extra_checks
                    ORDER BY create_date DESC";
        
                    $months = $wpdb->get_results(
                        $wpdb->prepare(
                            $sql
                        )
                    );
                    $month_count = count( $months );
                    if ( ! $month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) {
                        return;
                    }
                    $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
                    $wp_query = add_query_arg();
                    $wp_query = remove_query_arg('m');
                    $link = esc_url_raw($wp_query);
                    ?>
                    <div class="alignleft actions">
                    <select name="m" id="filter-by-date">
                        <option<?php selected( $m, 0 ); ?> value="0" data-rc="<?php _e($link); ?>"><?php _e( 'Todas las fechas' ); ?></option>
                    <?php
                    foreach ( $months as $arc_row ) {
                        if ( 0 == $arc_row->year ) {
                            continue;
                        }
                        $month = zeroise( $arc_row->month, 2 );
                        $year  = $arc_row->year;
                        $wp_query = add_query_arg('m', $arc_row->year . $month);
                        $link = esc_url_raw($wp_query);
                        printf(
                            "<option %s value='%s' data-rc='%s'>%s</option>\n",
                            selected( $m, $year . $month, false ),
                            esc_attr( $arc_row->year . $month ),
                            esc_attr( $link),
                            /* translators: 1: Nombre del mes, 2: Año de 4 dígitos. */
                            sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
                        );
                    }
                    ?>
                    </select>
                    <a href="javascript:void(0)" class="button" onclick="window.location.href = jQuery('#filter-by-date option:selected').data('rc');">Filtrar</a>
                    </div>
                    <?php
                break;
                break;
            case 'bottom':
                // Tu código HTML para mostrar
                break;
        }
    }
}
y para la acción de consulta, puedes agregar código en
public function prepare_items()
o función get_customers
$this->items = self::get_customers($per_page, $current_page);
<?php
        //public static function get_customers($per_page = 20, $page_number = 1) 
        if (!empty($_REQUEST['m'])) {
            $search = $_REQUEST['m'];
            $year = substr($search,0,4);
            $month = substr($search,4,5);
            if(!empty($year)){
                $sql .= ' And YEAR(create_date)="' . $year . '"';
            }
            if(!empty($month)){
                $sql .= ' And MONTH(create_date)="' . $month . '"';
            }
        }
Este es un ejemplo para obtener de mi base de datos
Cuando haces clic en el botón Filtrar irá a la URL desde data-rc

