Search code examples
wordpressadvanced-custom-fieldscustom-post-typewp-admin

wp-admin filter cpt by taxonomy and taxonomy relationship


I have Categories and Messages custom post types, both of them can have a custom taxonomy called Subcategories.

I would like to be able to filter Messages on wp-admin by their custom taxonomy, for example to show all Messages that are in Subcategory_1. I managed to do that with the code I've found here.

The problem is that I would also like to filter them by Category that has the same Subcategory as a Message.

Example:

  • Category_1 has Subcategory_1
  • Category_2 has Subcategory_2
  • Message_1 has Subcategory_1
  • Message_2 has Subcategory_1
  • Message_3 has Subcategory_2

Expected result would be that when user selects Category_1 from filter dropdown, only Message_1 and Message_2 are shown.

Current code(can only filter by Subcategory):

function todo_restrict_manage_posts() {
 global $typenow;
 $args=array( 'public' => true, '_builtin' => false ); 
 $post_types = get_post_types($args);
 if ( in_array($typenow, $post_types) ) {
 $filters = get_object_taxonomies($typenow);
    foreach ($filters as $tax_slug) {
        $tax_obj = get_taxonomy($tax_slug);
        wp_dropdown_categories(array(
            'show_option_all' => __('Show All '.$tax_obj->label ),
            'taxonomy' => $tax_slug,
            'name' => $tax_obj->name,
            'orderby' => 'term_order',
            'selected' => $_GET[$tax_obj->query_var],
            'hierarchical' => $tax_obj->hierarchical,
            'show_count' => true,
            'hide_empty' => false
        ));
    }
 }
}
function todo_convert_restrict($query) {
 global $pagenow;
 global $typenow;
 if ($pagenow=='edit.php') {
    $filters = get_object_taxonomies($typenow);
    foreach ($filters as $tax_slug) {
        $var = &$query->query_vars[$tax_slug];
        if ( isset($var) ) {
            $term = get_term_by('id',$var,$tax_slug);
            $var = $term->slug;
        }
    }
 }
 return $query;
}
 add_action( 'restrict_manage_posts', 'todo_restrict_manage_posts' );
 add_filter('parse_query','todo_convert_restrict');

Current filters (filter by Category missing)

Current filters (filter by Category missing)


Solution

  • If I understand you properly, and assuming that the Categories post type slug is categories and the Subcategories taxonomy slug is subcategories, the following should work for you:

    SNIPPET 1: This would add the "filter by Category" drop-down menu:

    add_action( 'restrict_manage_posts', 'add_categories_cpt_filter' );
    function add_categories_cpt_filter( $post_type ) {
        // Display the filter only on the Messages edit page.
        if ( 'messages' === $post_type ) {
            $posts = get_posts( [
                'post_type'      => 'categories',
                'posts_per_page' => 99999,
                'orderby'        => 'title',
                'order'          => 'ASC',
            ] );
    
            echo '<select name="categories_cpt" class="postform">';
    
            if ( ! empty( $posts ) ) {
                echo '<option value="0">Show All Categories</option>';
            } else {
                echo '<option value="0">No Categories</option>';
            }
    
            $selected = isset( $_GET['categories_cpt'] ) ?
                absint( $_GET['categories_cpt'] ) : 0;
            foreach ( $posts as $p ) {
                printf( '<option value="%s"%s>%s</option>',
                    $p->ID, selected( $selected, $p->ID, false ),
                    esc_html( $p->post_title ) );
            }
    
            echo '</select>';
        }
    }
    

    SNIPPET 2: This would filter the Messages by the selected Category's terms:

    add_action( 'pre_get_posts', 'filter_messages_by_categories_cpt' );
    function filter_messages_by_categories_cpt( $query ) {
        // Enable the filter only on the Messages edit page.
        if ( $query->is_admin && $query->is_main_query() &&
            ( $screen = get_current_screen() ) &&
            ( 'edit-messages' === $screen->id ) &&
            ! empty( $_GET['categories_cpt'] )
        ) {
            $tax_query = $query->get( 'tax_query' );
            $tax_query = is_array( $tax_query ) ? $tax_query : [];
    
            $term_ids = get_terms( [
                'taxonomy'   => 'subcategories',
                'object_ids' => absint( $_GET['categories_cpt'] ),
                'fields'     => 'ids',
            ] );
            if ( ! is_wp_error( $term_ids ) && ! empty( $term_ids ) ) {
                $tax_query[] = [
                    'taxonomy' => 'subcategories',
                    'terms'    => $term_ids,
                ];
    
                $query->set( 'tax_query', $tax_query );
            }
        }
    }
    

    You can add both the snippets above after the code in question, i.e. after the add_filter('parse_query','todo_convert_restrict');.