4

I have three custom post types set up, articles, videos and photos.
I am using standard categories for these post types, and sharing the categories across all post types.

I am trying to create a nav menu for each post type, listing the categories, that should follow the following structure:

  • Photos

    • Cat 1
    • Cat 3
    • Cat 5
  • Videos

    • Cat 2
    • Cat 3
    • Cat 5
  • Articles

    • Cat 1
    • Cat 2
    • Cat 4

Categories that do not contain the custom post type should be hidden.

get_categories() with hide_empty set to 1 is obviously close, but it doesn't allow you to specify a post type.

6

Put the following in your functions.php:

function wp_list_categories_for_post_type($post_type, $args = '') {
    $exclude = array();

    // Check ALL categories for posts of given post type
    foreach (get_categories() as $category) {
        $posts = get_posts(array('post_type' => $post_type, 'category' => $category->cat_ID));

        // If no posts found, ...
        if (empty($posts))
            // ...add category to exclude list
            $exclude[] = $category->cat_ID;
    }

    // Set up args
    if (! empty($exclude)) {
        $args .= ('' === $args) ? '' : '&';
        $args .= 'exclude='.implode(',', $exclude);
    }

    // List categories
    wp_list_categories($args);
}

Now you can call wp_list_categories_for_post_type('photos'); or wp_list_categories_for_post_type('videos', 'order=DESC&title_li=Cats'); and the like.

  • Thanks t f. Exactly what I was looking for. It didn't quite work to start with, you forgot to filter get_posts with the category. – kdev Apr 19 '13 at 14:22
  • Oh, that's right. On my demo WP site, I only have a single category, that's why I didn't notice. Glad you found the bug, fixed it, and now it works. – tfrommen Apr 19 '13 at 14:30
0

function for category by post. copy in function.php

function get_categories_by_post_type($post_type, $args = '') {
    $exclude = array();
    //check all categories and exclude
    foreach (get_categories($args) as $category) {
        $posts = get_posts(array('post_type' => $post_type, 'category' => $category->cat_ID));
        if (empty($posts)) { $exclude[] = $category->cat_ID; }
    }
    //re-evaluate args
    if (!empty($exclude)) {
        if(is_string($args)) {
            $args .= ('' === $args) ? '' : '&';
            $args .= 'exclude='.implode(',', $exclude);
        } else {
            $args['exclude'] = $exclude;
        }
    }
    return get_categories($args);
}
0

Updated this to be more flexible. Returns category objects, omitting all empty.

// Gets Categories by Post Type
// returns categories that aren't empty
function get_categories_for_post_type($post_type = 'post', $taxonomy = '') {
  $exclude = array();
  $args = array(
    "taxonomy" => $taxonomy,
  );
  $categories = get_categories($args);

  // Check ALL categories for posts of given post type
  foreach ($categories as $category) {
    $posts = get_posts(array(
      'post_type' => $post_type,
      'tax_query' => array(
        array(
          'taxonomy' => $taxonomy,
          'field' => 'term_id',
          'terms' => $category->term_id
        )
      )
    ));

    // If no posts in category, add to exclude list
    if (empty($posts)) {
      $exclude[] = $category->term_id;
    }
  }

  // If exclude list, add to args
  if (!empty($exclude)) {
    $args['exclude'] = implode(',', $exclude);
  }

  // List categories
  return get_categories($args);
}

add that to your functions.php

To use (for example, if populating a select for filtering):

<select>
<?php
$categories = get_categories_for_post_type('projects', 'project_categories');
foreach($categories as $category) { ?>
   <option value="<?php echo $category->slug; ?>">
       <?php echo $category->name; ?>
   </option>
<?php } ?>
</select>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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