Skip to content

Get categories by post type in WordPress

Get categories by post type

Have you ever wondered about how to get categories by post type? Categories is the default taxonomy for WordPress just like the tags. But categories have hierarchies and tags don’t have. Categories are used for the posts by default. However, if we register a new custom post type for which we wish to add categories, it’s fairly simple job to do.

Registering a new post type is a plugin functionality and we should always do it by creating a custom plugin. You may find the developers documentation for registering a custom post type here.


Practical scenario

Let’s say we have a custom post type “movies” which also shares the category with the posts. Now, we have categories “comedy“, “horror” and “adventure” for the movies posts and “stories“, “interviews“, “reviews” for the default posts. So, for both of the archives we want to show only those categories that relates to the post type itself.

The issues

There are several ways to get categories in WordPress. The get_categories() function will retrieve all the categories available in your site regardless of post type. Similarly wp_list_categories() and get_terms() can also fetch and display categories or terms. But none of them have the option to retrieve categories for a given post type.

Our goal to achieve

In this tutorial we shall discover how we can get categories by post type. In our test case, the movies posts archive will show only “comedy“, “horror” and “adventure” and the default posts archive will show “stories“, “interviews“, “reviews“. Let’s see how we shall do it.

Coding plans

To achieve this, we have to think in a different way. WordPress functions covers everything that you need to do in a development. We shall explore some of the functions for this.

The get_posts() function retrieve posts array which includes post ID. The get_the_terms() function retrieves the terms of the taxonomy that are attached to the post. Now, we have something to start with. Our approach will be to make a function which will do –

  1. Get all posts with get_posts() function.
  2. Then loop through the posts array and retrieve the terms attached to those posts with get_the_terms() function.
  3. Store the new terms objects within a new array.
  4. Use the new array and loop through to make a new array of terms with our desired key, value pair.
  5. Finally, make that array unique as duplicate entries can be there.
  6. Return the final array.

The code snippet to get categories by post type

Here is the full snippet that will retrieve categories or custom taxonomy terms by post type. You may place it in your theme’s function.php.

if( !function_exists('tartist__get_terms_by_post_type') ){

    function tartist__get_terms_by_post_type( $postType = 'post', $taxonomy = 'category'){

        /**
         * Get Terms by post type
         * @author Amit Biswas (https://templateartist.com/)
         * @link (https://templateartist.com/2020/05/18/get-categories-by-post-type-in-wordpress/)
         * 
         * @param postType default 'post'
         * @param taxonomy default 'category'
         * 
         * @return array of terms for a given $posttype and $taxonomy
         * @since 1.0.1
         * 
         * 1. Get all posts by post type
         * 2. Loop through the posts array and retrieve the terms attached to those posts
         * 3. Store the new terms objects within `$post_terms`
         * 4. loop through `$post_terms` as it's a array of term objects.
         * 5. store the terms with our desired key, value pair inside `$post_terms_array`
         */

        //1. Get all posts by post type
        $get_all_posts = get_posts( array(
            'post_type'     => esc_attr( $postType ),
            'post_status'   => 'publish',
            'numberposts'   => -1
        ) );

        if( !empty( $get_all_posts ) ){

            //First Empty Array to store the terms
            $post_terms = array();
            
            //2. Loop through the posts array and retrieve the terms attached to those posts
            foreach( $get_all_posts as $all_posts ){

                /**
                 * 3. Store the new terms objects within `$post_terms`
                 */
                $post_terms[] = get_the_terms( $all_posts->ID, esc_attr( $taxonomy ) );

            }

            //Second Empty Array to store final term data in key, value pair
            $post_terms_array = array();

            /**
             * 4. loop through `$post_terms` as it's a array of term objects.
             */

            foreach($post_terms as $new_arr){
                foreach($new_arr as $arr){

                    /**
                     * 5. store the terms with our desired key, value pair inside `$post_terms_array`
                     */
                    $post_terms_array[] = array(
                        'name'      => $arr->name,
                        'term_id'   => $arr->term_id,
                        'slug'      => $arr->slug,
                        'url'       => get_term_link( $arr->term_id )
                    );
                }
            }

            //6. Make that array unique as duplicate entries can be there
            $terms = array_unique($post_terms_array, SORT_REGULAR);

            //7. Return the final array
            return $terms;

        }

    }

}

The function tartist__get_terms_by_post_type() accepts 2 parameters. First parameter is for the post type, second parameter is for taxonomy. Both parameters are optional as the default value is set to post and category.

Return value

On success, this function returns the terms array for the given post type and taxonomy. Here is an example –

//store the categories for movies post type
$postTerms = tartist__get_terms_by_post_type('movies');
var_dump( $postTerms );

The above code will return the categories for movies. As per our example –

array (size=3)
    0 => 
        array (size=4)
        'name'    => string 'Comedy' (length=6)
        'term_id' => int 7
        'slug'    => string 'comedy' (length=6)
        'url'     => string 'http://example.com/category/comedy/' (length=35)
    1 => 
        array (size=4)
        'name'    => string 'Horror' (length=6)
        'term_id' => int 9
        'slug'    => string 'horror' (length=6)
        'url'     => string 'http://example.com/category/horror/' (length=35)
    2 => 
        array (size=4)
        'name'    => string 'Adventure' (length=9)
        'term_id' => int 10
        'slug'    => string 'adventure' (length=9)
        'url'     => string 'http://example.com/category/adventure/' (length=38)

How to use

There are many scopes where you can use this function. For instance, you can create a new widget that shows the categories by post type. Or you may use this to create interactive forms etc. It’s totally up to you, how you will use this in your project.

For example, let’s create an unordered list of terms with this –

<ul>
<?php
    //store the categories for movies post type
    $postTerms = tartist__get_terms_by_post_type('movies');
    
    if( !empty( $postTerms ) ){
        foreach( $postTerms as $term ){ 
?>
    <li><a href="<?php echo esc_url( $term['url'] ); ?>><?php echo esc_html( $term['name'] ); ?></a></li>
<?php } } ?>
</ul>

Special note

If you check our main function, at point 5 where we are making the $post_terms_array array, you can add anything that is relevant to terms. Here we have added the term archive url parameter which is not originally returned by the get_the_terms() function.

I have used this method in my latest Xgenie – Business Portfolio Theme in the portfolio and services archive page.

I hope you enjoyed the tutorial and if you find it helpful, I will appreciate if you share the article with others too. Thank you all.

Featured image by Martin Shreder on Unsplash


Leave a Reply