How to get dynamically custom post type that are under a certain category

I think you may accomplish this by using the following WP builtin features.

  • $wp_taxonomies – global object, it stores all registered taxonomies including default and custom
  • get_post_types() – get all registered post types including default and custom

To be simple, the examples for their usage are separated below. You may then combine them to create a registered post type list with conditions given such as constraining to certain post types. Then compare it with the registered taxonomies. Or you may create a preset list with post type names and registered taxonomies for comparison. Maybe you have some even better methods.

This is the example to get all the registered taxonomies for a specific post type.

<?php
// the taxonomy list is available in plugin, templates
// example, I put it it a plugin
global $wp_taxonomies;

    // loop through all custom taxonomy and use preg_match() to test which category is matched to the post_type
   // 1. assumed your post_type name is part of the category_name, for other checking logic, it is up to you and flexible
   // 2. the $post_type is passed somewhere, here is a hard-coded example

    $post_type="scusto";
    foreach ($wp_taxonomies as $key => $value) {
           if( preg_match( "/{$post_type}*/", $key ) ) {
                    $registered_tax = $wp_taxonomies[$key]->name;
           }
    }

    // then you have the registered category stored in $registered_tax for manipulation
?>

Here is the function to get all registered post types

<?php
    // all registered post types output in an array
    $post_types = get_post_types();
?>

Edited/Updated 2020/04/4, 23:47

Here is the ajax solutions making use of the above information together with a few ajax filters.
There are 4 things you need to prepare. The following is written in the form of plugin since it is actually from my plugin. You may turn to other form to meet the needs. While being as a demonstration, I have leave out all testing and validation such as nonce. You may need to add them in practical situation. And for details of AJAX in WordPress, you may refer to the Plugin Handbook

  • Although I have added the ajax listener filter for backend, this is just showing that it could be done so. No js is enqueued in the admin page. If you need, you may add the admin_enqueue_scripts filter

    1. prepare form to display for visitors
    2. receive ajax call and action
    3. javascript for button to interact and send action

This is what the example do according to the understanding to your questions:

  • display 3 lists, post type list, category list, post list
  • when select post type, it updates the category list
  • when select the category, it updates the post list

Here is the form

class q363151_category_list {
    public function __construct() {
        //---------------------------------------
        // ajax update list
        // action name = update_cat_list, update_post_list
        //---------------------------------------
        // for frontend
        // ajax update category list
        add_action( 'wp_ajax_nopriv_update_cat_list', array( $this, 'update_cat_list' ) ); // non-logged in user
        add_action( 'wp_ajax_update_cat_list', array( $this, 'update_cat_list' ) ); // logged in user

        // for backend
        add_action( 'admin_post_update_cat_list', array( $this, 'update_cat_list' ) );
        add_action( 'admin_post_nopriv_update_cat_list', array( $this, 'update_cat_list' ) );

        // ajax update post list
        add_action( 'wp_ajax_nopriv_update_post_list', array( $this, 'update_post_list' ) ); // non-logged in user
        add_action( 'wp_ajax_update_post_list', array( $this, 'update_post_list' ) ); // logged in user

        // for backend
        add_action( 'admin_post_update_post_list', array( $this, 'update_post_list' ) );
        add_action( 'admin_post_nopriv_update_post_list', array( $this, 'update_post_list' ) );

        // enqueue scripts
        // add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ), 1 );
        add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) );

        // output selection form
        add_action( 'somewhere_in_your_theme', array( $this, 'render_form_list' ) );
    }

    // load scripts if any
    public function scripts() {
        wp_enqueue_script( 'q363147-ajax-post-list', plugins_url( '/ajax-post-list.js', __FILE__  ), array( 'jquery' ), 't' . time(), true );

        // add ajax url
        $config = array( 
            'ajaxurl' => admin_url('admin-ajax.php'),
        );
        wp_localize_script('q363147-ajax-post-list', 'q363147', $config);
    }

    // receive action from ajax call
    public function update_cat_list() {
        global $wp_taxonomies;
        // Error Handling
        // check the nonce

        $post_type = $_POST['post_type'];

        // get taxonomy for specific post type
        $registered_tax = null;
        switch ( $post_type ) {
            case 'post':
                $tax_keyword = 'category';
            break;

            default:
                $tax_keyword = 'does_not_exist';
                break;
        }

        foreach ($wp_taxonomies as $key => $tax) {
            if( preg_match( "/^{$tax_keyword}/", $key ) ) {
                $registered_tax = $tax; // assumed 1 taxonomy
            }
       }

        // output

        // category list
        // get terms(category name) for the found taxonomy

        $category_options = $this->return_options( 'category' );
        if( ! empty( $registered_tax ) ) {
            $terms = get_terms([
                'taxonomy' => $registered_tax->name,
                'hide_empty' => false,
            ]);

            foreach ($terms as $key => $term) {
                $category_options .= '<option value="' . $term->name . '">' . $term->name . '</option>';
            }
        }

        // action here after checking
        wp_send_json_success( array(
            'message' => __( 'List data preparation completed', 'q363147' ),
            'post_type' => $post_type,
            'tax_keyword' => $tax_keyword,
            'taxonomy' => ( $registered_tax->name ),
            'tax_query_var' => ( $registered_tax->query_var ),
            'categories' => $category_options,

            // for debug
            'found_tax' => preg_match( "/^{$tax_keyword}/", $key ),
            'post_data' => $_POST, 
        ) );
    }

    public function update_post_list() {
        $post_type = $_POST['post_type'];
        $category = $_POST['selected_category'];
        $tax_query_var = $_POST['tax_query_var'];
        // $taxonomy = $_POST['taxonomy']; // for later extension

        // post query
        $args = array( 
            'post_type' => $post_type,
            'category_name' => $category,
        );
        $query = new WP_Query( $args );
        $posts = $query->posts;

        $post_options = $this->return_options( 'post' );
        foreach ($posts as $key => $post) {
            $post_options .= '<option value="' . $post->ID . '">' . $post->post_title . '</option>';
        }

        // action here after checking
        wp_send_json_success( array(
            'message' => __( 'Post list preparation completed', 'q363147' ),
            'post_type' => $post_type,
            'tax_query_var' => $tax_query_var,
            'posts' => $post_options,

            // for debug
            'query_result' => $query,
            'post_data' => $_POST, 
        ) );
    }

    // render form to frontend
    public function render_form_list() {
        // output your form here

        $post_types = get_post_types();

        // if you want to limit the following post type to be appeared
        $allowed_to_search_arr = array(
            'post' => 'Post',
            'page' => 'Page', // if you turned it on for search, default is off
            'custom1' => 'Custom post type1',
            'custom2' => 'Custom post type2',
        );

        // post type list
        $post_type_options="<option value="default">-- Post types --</option>";
        foreach ($post_types as $key => $post_type) {
            if( array_key_exists( $post_type, $allowed_to_search_arr ) )
            $post_type_options .= '<option value="' . $key . '">' . $post_type . '</option>';
        }

        // category list
        $category_options = $this->return_options( 'category' );

        // post list
        $post_options = $this->return_options( 'post' );

        $form = <<<HTML
        <form method="post" class="form">
            <ul class="list">
                <li class="post-types">
                    <select name="post_type" class="post-type-selector">
                        <!-- send action to ajax -->
                        $post_type_options
                    </select>
                </li>

                <li class="categories">
                    <select name="selected_category" class="category-selector">
                        <!-- respond to ajax result -->
                        $category_options
                    </select>
                </li>

                <li class="posts">
                    <select name="selected_post" class="post-selector">
                        <!-- respond to ajax result -->
                        $post_options
                    </select>
                </li>
            </ul>
            <input type="hidden" name="taxonomy">
            <input type="hidden" name="tax_query_var">
        </form>
HTML;

        echo $form;
    }

    private function return_options( $name="" ) {
        return '<option value="default">-- Please select ' . $name . ' --</option>';
    }
}

// create new object
new q363151_category_list();

ajax handler

(function ($) {
  // post type selector
  $('.form').on('change', '.post-type-selector', function (e) {
    var $initiator = $(this); // Error Handling
    $initiator.prop('disabled', true); // set ajax data, Object for the AJAX action

    var data = {
      'action': 'update_cat_list',
      'post_type': $('select[name="post_type"]').val(),
    }; // send ajax

    $.ajax({
        type: 'post',
        url: q363147.ajaxurl,
        dataType: 'json',
        data: data
    }).done( function( response ) {
        console.log(response);
        $initiator.prop('disabled', false);

        // update category list
        $( '.category-selector' ).html( response.data.categories );

        // update taxonomy
        $( '[name="taxonomy"]').val(response.data.taxonomy);
        $( '[name="tax_query_var"]').val(response.data.tax_query_var);

    }).fail( function( jqXHR ) {
        // failure handling
    });
  });

  // category selector
  $('.form').on('change', '.category-selector', function (e) {
    var $initiator = $(this); // Error Handling
    $initiator.prop('disabled', true); // set ajax data, Object for the AJAX action

    var data = {
      'action': 'update_post_list',
      'post_type': $('select[name="post_type"]').val(),
      'selected_category': $('select[name="selected_category"]').val(),
      'tax_query_var': $('[name="tax_query_var"]').val(),
    }; // send ajax

    $.ajax({
        type: 'post',
        url: q363147.ajaxurl,
        dataType: 'json',
        data: data
    }).done( function( response ) {
        console.log(response);
        $initiator.prop('disabled', false);

        // update post list
        $( '.post-selector' ).html( response.data.posts );
    }).fail( function( jqXHR ) {
        // failure handling
    });
  });
})(jQuery);

Display to visitors

// place anywhere in your template
do_action( 'somewhere_in_your_theme' );

Here is the screenshots for the working of this example.
[Select a post type1
[Select a category of the post type]2
[Select a post to work with]3

Leave a Comment