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
3

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.

2
  • 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
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
}

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