Get categories by post type in WordPress
Tags: wordpress, tutorial, php, snippet
Published: 2020-05-22 11:35:15

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:more -->
<!--more-->
<!-- /wp:more -->

<!-- wp:paragraph -->
<p>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 <a href="https://developer.wordpress.org/reference/functions/register_post_type/">registering a custom post type</a> here.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Practical scenario</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Let's say we have a custom post type "<em>movies</em>" which also shares the category with the posts. Now, we have categories "<strong>comedy</strong>", "<strong>horror</strong>" and "<strong>adventure</strong>" for the <em><span style="text-decoration: underline;">movies</span></em> posts and "<strong>stories</strong>", "<strong>interviews</strong>", "<strong>reviews</strong>" for the default <em><span style="text-decoration: underline;">post</span></em>s. So, for both of the archives we want to show only those categories that relates to the post type itself.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>The issues</h3>
<!-- /wp:heading -->

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

<!-- wp:heading {"level":3} -->
<h3>Our goal to achieve</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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 "<strong>comedy</strong>", "<strong>horror</strong>" and "<strong>adventure</strong>" and the default posts archive will show "<strong>stories</strong>", "<strong>interviews</strong>", "<strong>reviews</strong>". Let's see how we shall do it.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Coding plans</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>The <code>get_posts()</code> function retrieve posts array which includes <strong>post ID</strong>. The <code>get_the_terms()</code> 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 -</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Get all posts with <code>get_posts()</code> function.</li><li>Then loop through the posts array and retrieve the terms attached to those posts with <code>get_the_terms()</code> function.</li><li>Store the new terms objects within a new array.</li><li>Use the new array and loop through to make a new array of terms with our desired <strong>key</strong>, <strong>value</strong> pair.</li><li>Finally, make that array unique as duplicate entries can be there.</li><li>Return the final array.</li></ol>
<!-- /wp:list -->

<!-- wp:heading -->
<h2>The code snippet to get categories by post type</h2>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>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&#91;] = 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&#91;] = 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;

        }

    }

}</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The function <code>tartist__get_terms_by_post_type()</code> 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 <em>post</em> and <em>category</em>.</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":3} -->
<h3>Return value</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>On success, this function returns the terms array for the given post type and taxonomy. Here is an example -</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>//store the categories for movies post type
$postTerms = tartist__get_terms_by_post_type('movies');
var_dump( $postTerms );</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>The above code will return the categories for <em>movies</em>. As per our example -</p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>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)</code></pre>
<!-- /wp:code -->

<!-- wp:heading {"level":3} -->
<h3>How to use</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>For example, let's create an unordered list of terms with this - </p>
<!-- /wp:paragraph -->

<!-- wp:code -->
<pre class="wp-block-code"><code>&lt;ul>
&lt;?php
    //store the categories for movies post type
    $postTerms = tartist__get_terms_by_post_type('movies');
    
    if( !empty( $postTerms ) ){
        foreach( $postTerms as $term ){ 
?>
    &lt;li>&lt;a href="&lt;?php echo esc_url( $term&#91;'url'] ); ?>>&lt;?php echo esc_html( $term&#91;'name'] ); ?>&lt;/a>&lt;/li>
&lt;?php } } ?>
&lt;/ul></code></pre>
<!-- /wp:code -->

<!-- wp:heading {"level":3} -->
<h3>Special note</h3>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>If you check our main function, at point 5 where we are making the <code>$post_terms_array</code> 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 <code>get_the_terms()</code> function.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>I have used this method in my latest <a href="https://templateartist.com/product/xgenie-business-portfolio-theme/">Xgenie – Business Portfolio Theme</a> in the portfolio and services archive page.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>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.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Featured image by&nbsp;<a href="https://unsplash.com/@martinshreder?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Martin Shreder</a>&nbsp;on&nbsp;<a href="https://unsplash.com/s/photos/technology?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<!-- /wp:paragraph -->