Skip to content

Instantly share code, notes, and snippets.

@kjbenk
Created May 30, 2017 12:31
Show Gist options
  • Save kjbenk/fd201925ee85b2fe5f90013761786e74 to your computer and use it in GitHub Desktop.
Save kjbenk/fd201925ee85b2fe5f90013761786e74 to your computer and use it in GitHub Desktop.
WP: All Post Filters Class
<?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