Created
May 30, 2017 12:31
-
-
Save kjbenk/fd201925ee85b2fe5f90013761786e74 to your computer and use it in GitHub Desktop.
WP: All Post Filters Class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Add custom post filters on the All Posts page. This is best used with an Elastic | |
* Search powered site since this can create a very expensive query. | |
* | |
* Simple Example: | |
* | |
* add_filter( 'all_post_filters', function ( $filters, $post_type ) { | |
* // Perform post type validiation. | |
* if ( ! in_array( $post_type, [ 'post' ], true ) ) { | |
* return $filters; | |
* } | |
* | |
* // Custom field. | |
* $custom_field_options = [ | |
* 'key' => 'value', | |
* 'key' => 'value', | |
* 'key' => 'value', | |
* ]; | |
* | |
* $filters[] = array( | |
* 'id' => 'custom_field', | |
* 'name' => __( 'Custom field' ), | |
* 'field' => 'meta', | |
* 'no_option' => __( 'Any field' ), | |
* 'options' => $custom_field_options, | |
* ); | |
* | |
* return $filters; | |
* }, 10, 2 ); | |
*/ | |
class All_Post_Filters { | |
/** | |
* The filters. | |
* | |
* @var array | |
*/ | |
private $_filters = array(); | |
/** | |
* Instances of child classes. | |
* | |
* @var array | |
*/ | |
protected static $instances = array(); | |
/** | |
* Initialization typically happens via get_instance() method. | |
*/ | |
public function __construct() {} | |
/** | |
* Return an instance of a child class. | |
* | |
* @return All_Post_Filters | |
*/ | |
public static function get_instance() { | |
$class = get_called_class(); | |
if ( ! isset( self::$instances[ $class ] ) ) { | |
self::$instances[ $class ] = new static; | |
self::$instances[ $class ]->setup(); | |
} | |
return self::$instances[ $class ]; | |
} | |
/** | |
* Perform initial setup. | |
*/ | |
public function setup() { | |
// Get the current filters. | |
add_action( 'admin_init', array( $this, 'get_filters' ) ); | |
add_action( 'restrict_manage_posts', array( $this, 'add_filters' ) ); | |
add_action( 'pre_get_posts', array( $this, 'filter_posts' ) ); | |
add_action( 'parse_query', array( $this, 'parse_query' ) ); | |
add_filter( 'disable_months_dropdown', '__return_true' ); | |
add_filter( 'disable_categories_dropdown', '__return_true' ); | |
} | |
/** | |
* Add all of our custom filters. | |
* | |
* @param string $post_type The post type slug. | |
*/ | |
public function add_filters( $post_type ) { | |
// Check to see if we have any filters to add. | |
$filters = $this->_filters; | |
if ( empty( $filters ) ) { | |
return; | |
} | |
foreach ( $filters as $filter ) { | |
$this->_the_screen_reader_label( $filter ); | |
$this->_the_select_dropdown( $filter ); | |
} | |
} | |
/** | |
* Get the filters at the proper time. | |
*/ | |
public function get_filters() { | |
$this->_filters = $this->_get_filters(); | |
} | |
/** | |
* Parse the query to remove the empty search param. | |
* | |
* @param WP_Query &$this The WP_Query instance (passed by reference). | |
*/ | |
public function parse_query( $query ) { | |
// Remove the search param if it is not set. | |
if ( isset( $query->query['s'] ) && empty( $query->query['s'] ) ) { | |
unset( $query->query['s'] ); | |
$query->is_search = false; | |
} | |
} | |
/** | |
* Filter the admin All posts page based on the custom filters. | |
* | |
* @param WP_Query $query The current query object. | |
*/ | |
public function filter_posts( $query ) { | |
// Check to see if we have any filters to add. | |
$filters = $this->_filters; | |
if ( empty( $filters ) ) { | |
return; | |
} | |
global $pagenow; | |
// Make sure we are only modifing the All posts page query. | |
if ( | |
is_admin() | |
&& in_array( $query->get( 'post_type' ), $this->post_types(), true ) | |
&& 'edit.php' === $pagenow | |
) { | |
foreach ( $filters as $filter ) { | |
// Skip this filter is no value is passed. | |
if ( empty( $_GET[ $filter['id'] ] ) ) { // WPCS: input var okay. CSRF okay. | |
continue; | |
} | |
// Skip this filter is no field type is passed. | |
if ( empty( $filter['field'] ) ) { | |
continue; | |
} | |
// Suppress Filters | |
$query->set( 'suppress_filters', true ); | |
// Get value. | |
$value = $this->get_sanitized_value( $filter['id'], 0 ); | |
// Meta query | |
if ( 'meta' === $filter['field'] ) { | |
/** | |
* Filter the meta query. | |
* | |
* @param array The meta query. | |
* @param string The field ID. | |
*/ | |
$meta_query = apply_filters( 'all_posts_filter_meta_query', array( | |
'key' => $filter['id'], | |
'compare' => ! empty( $filter['multiple'] ) ? 'IN' : '=', | |
'value' => $value, | |
), $filter['id'] ); | |
if ( is_array( $query->get( 'meta_query' ) ) ) { | |
$meta_query = array_merge( $query->get( 'meta_query' ), array( | |
$meta_query, | |
) ); | |
} else { | |
$meta_query = array( $meta_query ); | |
} | |
$query->set( 'meta_query', $meta_query ); | |
} | |
// Date query | |
if ( 'date' === $filter['field'] ) { | |
/** | |
* Filter the date query. | |
* | |
* @param array The date query. | |
* @param string The field ID. | |
*/ | |
$date_query = apply_filters( 'all_posts_filter_date_query', array( | |
'after' => $value, | |
), $filter['id'] ); | |
if ( is_array( $query->get( 'date_query' ) ) ) { | |
$date_query = array_merge( $query->get( 'date_query' ), array( | |
$date_query, | |
) ); | |
} else { | |
$date_query = array( $date_query ); | |
} | |
$query->set( 'date_query', $date_query ); | |
} | |
// Tax query | |
if ( 'tax' === $filter['field'] ) { | |
/** | |
* Filter the tax query. | |
* | |
* @param array The date query. | |
* @param string The field ID. | |
*/ | |
$tax_query = apply_filters( 'all_posts_filter_tax_query', array( | |
'taxonomy' => $filter['tax'], | |
'terms' => $value, | |
), $filter['id'] ); | |
if ( is_array( $query->get( 'tax_query' ) ) ) { | |
$tax_query = array_merge( $query->get( 'tax_query' ), array( | |
$tax_query, | |
) ); | |
} else { | |
$tax_query = array( $tax_query ); | |
} | |
$query->set( 'tax_query', $tax_query ); | |
} | |
// Post | |
if ( 'post' === $filter['field'] ) { | |
/** | |
* Filter the post query. | |
* | |
* @param array The post query. | |
* @param string The field ID. | |
*/ | |
$post_query = apply_filters( | |
'all_posts_filter_post_query', | |
$value, | |
$filter['id'] | |
); | |
$query->set( $filter['id'], $post_query ); | |
} | |
/** | |
* Alter the actual query. | |
* | |
* @param string The field ID. | |
*/ | |
$query = apply_filters( 'all_posts_filter_query', $query, $filter['id'] ); | |
} // End foreach(). | |
} // End if(). | |
} | |
/** | |
* Get the current filters. | |
* | |
* @return array Filtered list of post filters. | |
*/ | |
private function _get_filters() { | |
$post_type = 'post'; | |
if ( isset( $_GET['post_type'] ) ) { // WPCS: input var okay. | |
$post_type = sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); // WPCS: input var okay. | |
} | |
/** | |
* Allow other parts of the site to add filters. | |
* | |
* @param array $this->_filters List of filters. | |
* @param string $post_type The current post type. | |
*/ | |
return apply_filters( 'all_post_filters', $this->_filters, $post_type ); | |
} | |
/** | |
* Output the filter's screen reader text. | |
* | |
* @param array $filter The current filter. | |
* @return string HTML string. | |
*/ | |
private function _the_screen_reader_label( $filter ) { | |
echo '<label class="screen-reader-text" for="' . esc_attr( $filter['id'] ) . '">' . | |
esc_html__( 'Filter by' ) . ' ' . esc_attr( $filter['name'] ) . | |
'</label>'; | |
} | |
/** | |
* Output the filter's select dropdown. | |
* | |
* @param array $filter The current filter. | |
* @return string HTML string. | |
*/ | |
private function _the_select_dropdown( $filter ) { | |
// Custom HTML. | |
if ( ! empty( $filter['html'] ) ) { | |
echo $filter['html']; | |
return; | |
} | |
// Get the currently selected value. | |
$current_selection = $this->get_sanitized_value( $filter['id'], 0 ); | |
// Output the setting. | |
echo '<select name="' . esc_attr( $filter['id'] ) . '" id="' . esc_attr( $filter['id'] ) . '" class="postform">'; | |
// Add a default option | |
if ( false !== $filter['no_option'] ) { | |
echo '<option value>' . esc_html( $filter['no_option'] ) . '</option>'; | |
} | |
// Add options | |
foreach ( $filter['options'] as $value => $name ) { | |
echo '<option value="' . esc_attr( $value ) . '" ' . $this->selected( $current_selection, $value ) . '>' . esc_html( $name ) . '</option>'; | |
} | |
echo '</select>'; | |
} | |
/** | |
* Get sanitized value based on value type. | |
* | |
* @param string $id The filter id. | |
* @return mixed The sanitized value. | |
*/ | |
public function get_sanitized_value( string $id, $default = null ) { | |
if ( empty( $_GET[ $id ] ) ) { | |
return $default; | |
} | |
if ( is_array( $_GET[ $id ] ) ) { | |
$santizied_value = array_map( 'sanitize_text_field', wp_unslash( $_GET[ $id ] ) ); | |
} else { | |
$santizied_value = sanitize_text_field( wp_unslash( $_GET[ $id ] ) ); | |
} | |
return $santizied_value; | |
} | |
/** | |
* Determine the selection of a select field. | |
* | |
* @param mixed $current The current value. | |
* @param string $value The option value. | |
* @return string HTML string. | |
*/ | |
public function selected( $current, $value ) { | |
if ( is_array( $current ) ) { | |
return in_array( $value, $current ) ? 'selected="selected"' : ''; | |
} | |
return selected( $current, $value, false ); | |
} | |
/** | |
* The valid pst types to display the filters on. | |
* | |
* @return array Valid post types. | |
*/ | |
public function post_types() { | |
return [ 'post' ]; | |
} | |
} | |
All_Post_Filters::get_instance(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment