Testing if a post is in a descendant category in WordPress

You can find a solution to this problem in WordPress Codex. The only issue being that it only allows you to pass a single post ID or an array of post IDs, not allowing you to use your category slug, not to mention multiple slugs.

The documentation of this function tells you to see get_term_by() if you want to use category name or slug, but why not extend this function to do this for you?

Below you can find the implementation I use to solve this problem:

<?php
/**
 * Tests if any of a post's assigned categories are descendants of target categories
 * Modified to also accept category slug or array of slugs.
 *
 * @param int|string|array $cats The target categories. Integer ID, slug string or array of integer IDs or slug strings
 * @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
 * @uses get_term_by() Passes $cats if they are strings
 * @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
			if ( is_string( $cat ) ) {
				$cat = get_term_by( 'slug', $cat, 'category' );
				if ( ! isset( $cat, $cat->term_id ) )
					continue;
				$cat = $cat->term_id;
			}
			$descendants = get_term_children( (int) $cat, 'category' );
			if ( $descendants && in_category( $descendants, $_post ) )
				return true;
		}
		return false;
	}
}

The only modification I made is the if ( is_string( $cat ) ) condition. If you’d rather use category names in favour of the slug, change 'slug' to 'name' in get_term_by() call.

Usage

The example below is the example taken from Codex, only modified to use category slug instead of ID.

// Post is assigned to "fruit" category or any descendant of "fruit" category?
<?php if ( in_category( 'fruit' ) || post_is_in_descendant_category( 'fruit' ) ) {
	// These are all fruits...
}
?>

Comments

  1. AJ on

    Major thanks! The code on the WordPress Codex was a good start but they neglected to provide a solution like you did here for using names instead of ID’s. Took me 6 hours to find a solution by finding yours here. I would recommend submitting this to WordPress and making sure you get credit for this.

    Reply
  2. DK on

    Thank you so much for this. I spent hours looking for the right solution as well. This was perfect!

    Reply
  3. Steve on

    After several hours I finally got this to code to work. Could someone tell me why I had to change “in_category” to “is_category” in the function itself?

    Reply
  4. Csaba on

    Thanks Michał, excellent code snippet and (for me) it worked right “out of the box”!

    Reply

Leave a Reply