11

In a site I'm developing, I have the following category structure:

* movies (parent)
    * thriller (child)
    * comedy (child)
    * drama (child)

The current post is in the comedy category. The has_term function with the following parameters returns true:

has_term( 'comedy', 'category' )

But, the same function with the following parameters returns false:

has_term( 'movies', 'category' )

My question is, is there a core function to check if the current post is in any child category of a specific parent category? If not, how can I check this?

Thanks in advance

0

3 Answers 3

19
+100

Add the following to your theme's functions.php:

/**
 * Tests if any of a post's assigned categories are descendants of target categories
 *
 * @param int|array $cats The target categories. Integer ID or array of integer IDs
 * @param int|object $_post The post. Omit to test the current post in the Loop or main query
 * @return bool True if at least 1 of the post's categories is a descendant of any of the target categories
 * @see get_term_by() You can get a category by name or slug, then pass ID to this function
 * @uses get_term_children() Passes $cats
 * @uses in_category() Passes $_post (can be empty)
 * @version 2.7
 * @link http://codex.wordpress.org/Function_Reference/in_category#Testing_if_a_post_is_in_a_descendant_category
 */
if ( ! function_exists( 'post_is_in_descendant_category' ) ) {
    function post_is_in_descendant_category( $cats, $_post = null ) {
        foreach ( (array) $cats as $cat ) {
            // get_term_children() accepts integer ID only
            $descendants = get_term_children( (int) $cat, 'category' );
            if ( $descendants && in_category( $descendants, $_post ) )
                return true;
        }
        return false;
    }
}

Use the function to check the parent category ID, not name or slug. I.e. if the 'movies' category ID is 50:

if ( post_is_in_descendant_category( 50 ) ) {
    // do something
}

If you don't know the 'movies' category ID, you could retrieve the ID using get_term_by() and pass it to post_is_in_descendant_category():

$category_to_check = get_term_by( 'name', 'movies', 'category' );

if ( post_is_in_descendant_category( $category_to_check->term_id ) ) {
    // do something
}
1
  • Thank you. This should be a built-in wordpress function. Commented Sep 6, 2020 at 19:07
4

If you want any nesting depth, use get_posts.

/**
 * Checks if the post is in one of the categories or any child category. 
 * 
 * @param  int|string|array $category_ids (Single category id) or (comma separated string or array of category ids).
 * @param  int              $post_id      Post ID to check. Default to `get_the_ID()`.
 * @return bool true, iff post is in any category or child category.
 */
function is_post_in_category( $category_ids, $post_id = null ) {
    $args = array(
        'include'  => $post_id ?? get_the_ID(),
        'category' => $category_ids,
        'fields'   => 'ids',
    );
    return 0 < count( get_posts( $args ) );
}

You can expand on this function in many ways. Maybe pass an array of post ids and filter out some or allow the query to be refined.

3
  • better answer as it checks for any depth.
    – JJS
    Commented Oct 9, 2021 at 20:55
  • this seems overkill
    – DrLightman
    Commented Oct 13, 2022 at 9:13
  • Although it might seem like overkill, I think this might result in fewer SQL queries than the other answer Commented Sep 13 at 0:51
1

Update for using custom taxonomies:

/**
 * Tests if any of a post's assigned categories are descendants of target categories
 *
 * @param int|array $cats The target categories. Integer ID or array of integer IDs
 * @param int|object $_post The post. Omit to test the current post in the Loop or main query
 * @param string $target_cat The taxonomy name to query for. Omit to use the default 'category' taxonomy used for posts
 * @return bool True if at least 1 of the post's categories is a descendant of any of the target categories
 * @see get_term_by() You can get a category by name or slug, then pass ID to this function
 * @uses get_term_children() Passes $cats
 * @uses in_category() Passes $_post (can be empty)
 * @version 2.7
 * @link https://developer.wordpress.org/reference/functions/in_category/#comment-4999
 */
if(!function_exists('post_is_in_descendant_category')) {
  function post_is_in_descendant_category($cats, $_post = null, $target_cat = 'category') {
    foreach((array) $cats as $cat) {
      // get_term_children() accepts integer ID only
      $descendants = get_term_children((int) $cat, $target_cat);
      if($descendants && has_term($descendants, $target_cat, $_post)) {
        return true;
      }
    }
    return false;
  }
}

Some context

in_category uses has_category which in turn calls has_term( $category, 'category', $post ) with the hard-coded 'category' being set as taxonomy.

Using has_term() directly allows us to check for a custom taxonomy like product_cat (WooCommerce) or any other taxonomy.

The function uses category as default so this can be used as a drop-in replacement.

Usage

Call the function like this:

if(post_is_in_descendant_category($category_to_check->term_id, $post, 'product_cat')) {
  // do something
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.