Category Functions for WordPress

[ Downtown Seattle WA 2012 ] My previous theme sports the now-infamous colorized categories, which aim to help visitors navigate featured content. In addition to the colors, featured categories display contextually relevant navigation, popular posts, and related tags. It’s a great way to improve organization and get more of your content in front of the visitor.

To make it happen, a variety of tasty WordPress code snippets are used, including versatile theme functions that enable getting the first category link, displaying sub-categories of the current category, displaying popular posts per category, related tags, and more. In this post, you get the entire collection, plucked directly from my theme’s functions.php file and mixed with everything needed to enhance WordPress’ category functionality for your own projects.

Menu

Custom body_class & post_class

WordPress makes it easy to add class to your web pages. Class attributes enable you to more easily and efficiently style your pages using CSS. Using the body_class() and post_class() functions, it’s possible to include any class names virtually anywhere in your WordPress theme. Note that body_class() is generally applied to the <body> element, while post_class() is more flexible, aiming for a post-enclosing element such as <article> or whatever makes sense.

There are (at least) two ways to add classes using these functions. First, if you just want to add a class name to all pages on your site, simply include it within the template tag, like so:

<body <?php body_class('custom-body-class'); ?>>
<article <?php post_class('custom-post-class'); ?>>

When working with categories, it’s often necessary to add classes conditionally, which is done by filtering the function via the theme’s functions.php file. Here is a simple example showing each function:

// add custom body class
add_filter('body_class', 'add_custom_body_class');
function add_custom_body_class($classes) {
	if (is_page()) $classes[] = 'custom-body-class';
	return $classes;
}
// add custom post class
add_filter('post_class', 'add_custom_post_class');
function add_custom_post_class($classes) {
	if (is_page()) $classes[] = 'custom-post-class';
	return $classes;
}

These functions respectfully add custom-body-class to the <body> element, and custom-post-class to the <article> element (or whatever element chosen for post_class). This is entirely plug-n-play functionality, just pick your class names and you’re all set. Of course, more advanced filtering is possible. For example, in my theme with the color-coded categories, custom classes are required for a multitude of different page views. Fortunately, we can combine the power of WordPress conditional tags with body_class() and post_class() filtering to get as specific as needed. To share an example of how elaborate it can get, here is the actual code used for my previous theme:

// custom body_class treatments
add_filter('body_class','body_class_names');
function body_class_names($classes) {
	// single
	if ((is_page()) || (is_404())) $classes[] = 'single';
	if (is_single()) $category = get_the_category(); $cat_slug = $category[0]->slug; $classes[] = 'single-feat-cat-'. $cat_slug;
	if ((is_single()) && (in_category(array('wordpress', 'security', 'seo', 'css', 'php', 'html', 'javascript', 'htaccess')))) $classes[] = 'single-feat-cat';
	// cats
	if (is_category(array('media', 'web-design'))) $classes[] = 'cat-parent';
	if (is_category(array('blogging', 'graphics', 'music', 'photos', 'tech', 'writing'))) $classes[] = 'media-sub-cat';
	if (is_category(array('wordpress', 'security', 'seo', 'css', 'php', 'html', 'javascript', 'htaccess'))) $classes[] = 'featured-cat';
	if ((is_author() && !is_paged()) || is_category(array('blogging', 'graphics', 'music', 'photos', 'tech', 'writing', 'media', 'web-design'))) $classes[] = 'cat-other';
	// archive
	if (is_year()) $classes[] = 'archive-year';
	return $classes;
}

// custom post_class treatments
add_filter('post_class','post_class_names');
function post_class_names($classes) {
	if (!in_category(array('wordpress', 'security', 'seo', 'css', 'php', 'html', 'javascript', 'htaccess'))) $classes[] = 'regular-cat';
	$category = get_the_category(); $cat_slug = $category[0]->slug; $classes[] = 'feat-cat-'. $cat_slug;
	if (is_page()) $classes[] = 'post';
	return $classes;
}

I’ll spare everyone the drama of explaining all of that, but suffice it to say that you can combine different conditional tags to target virtually any type of page. Very useful when working with WordPress categories.

Category-specific post thumbnails

WordPress automates the process of adding post thumbnails (featured images) to your posts, but currently there is no built-in way to choose featured images for categories. The easiest way of including custom images for your categories is to add something like this to the WordPress loop:

<?php // category featured image
$category = get_the_category(); 
echo '<img src="'. get_bloginfo('template_directory') .'/file/path/'. $category[0]->cat_name .'.png">';
?>

That works a treat, just specify the correct path to your category images directory and maybe change the file extension if you’re using a format other than PNG. As-is, the code uses the category name as the file name, so make sure you name your images accordingly. Note that categories containing blank spaces and other weird characters present issues on certain setups, so to workaround this you may want to try replacing $category[0]->cat_name with $category[0]->category_nicename or similar.

Another option is to display the featured category image only if a post thumbnail doesn’t exist. To do so, add the following code to your theme’s functions.php file:

// category-specific post thumbnails
function category_specific_post_thumbnails() {
	if ((function_exists('has_post_thumbnail')) && (has_post_thumbnail())) { 
		the_post_thumbnail('thumbnail');
	} else {
		$category = get_the_category();
		echo '<img src="'. get_bloginfo('template_directory') .'/file/path/'. $category[0]->cat_name .'.png">'; 
	}
}

As before, specify the correct path to your properly named category images and you’re all set. Once the code is place, you may display the output anywhere within the loop using the following template tag:

<?php category_specific_post_thumbnails(); ?>

For more advanced configurations and customizations, you’re probably better off going with a category image plugin.

Display link to first category, parent category, both

There are a number of ways to display all categories for each post, but sometimes you just want to display the first category when multiple are available. This is easily done with the following line placed in the loop:

$category = get_the_category();
echo $category[0]->name;

To make it a link, we call upon get_category_link():

$category = get_the_category();
$category_link = get_category_link($category->term_id);
echo '<a href="'. $category_link .'">'. $category[0]->name .'</a>';

This works great, but for my previous theme I want to display a link to the parent category, if it exists, so I added this to my functions.php file:

// display first category
function first_category_link() {
	$category = get_the_category();
	$category_link = get_category_link($category->term_id);
	$parent = get_cat_name($category[0]->category_parent);
	$parent_ID = get_cat_ID($parent);
	$parent_link = get_category_link($parent_ID);
	if ($parent_link) {
		echo '<a href="'. $parent_link .'">'. $parent .'</a>';
	} else {
		echo '<a href="'. $category_link .'">'. $category[0]->name .'</a>';
	}
}

And then call the function by placing the following template tag anywhere within the WordPress loop:

<?php first_category_link(); ?>

No editing is required for this snippet to work, although you may want to fiddle with the markup. You know, to make it just perfect for your project.

Display sub-categories of current category

The next trick I needed to do involves displaying the sub-categories of whatever current category the visitor happened to be viewing. This provides context to the user and facilitates expedient site navigation. To add this functionality to your theme, place the following in functions.php:

// display sub-categories of current category
function display_sub_categories() {
	$current_cat = get_query_var('cat');
	$args = array('child_of'=>$current_cat);
	$categories = get_categories($args);
	if (!empty($categories)) {
		echo '<ul>';
		foreach ($categories as $category) {
			echo '<li><a href="'.$category->slug.'">'. $category->name .'</a></li>';
		}
		echo '</ul>';
	} else {
		// fallback for when no subcategories are available
	}
}

Once this code is in place, call the function anywhere in your theme using the following template tag:

<?php display_sub_categories(); ?>

This method is easily modified to produce just about any category list you could imagine. To get an idea of the possibilities, visit the WordPress Codex and check out the many parameters at your disposal when using the get_categories() function. Powerful stuff!

Popular posts per category

Popular posts are good, no question about that, but for sites (such as this one) with lots of content in different categories, you can take it a step further and provide top posts that are relevant to the current category. So for example, when a visitor is viewing your WordPress Category, you can share with them the most popular WordPress posts instead of a bunch of stuff that might be less interesting.

To display popular posts per category, add the following snippet to your theme’s functions.php file:

// popular posts per category
function popular_posts_per_category() {
	global $post;
	$categories = get_the_category();
	foreach($categories as $category) {
		$cats[] = $category->cat_ID;
	}
	$cats_unique = array_unique($cats);
	$args = array(
		'category__in' => $cats_unique,
		'orderby' => 'comment_count',
		'order' => 'DESC',
		'post_type' => 'post',
		'post_status' => 'publish',
		'posts_per_page' => 5
	);
	echo '<ul>';
	$popular_posts = null;
	$popular_posts = new WP_Query($args);
	while ($popular_posts->have_posts()) : $popular_posts->the_post(); 
		$title_trim = get_the_title();
		if (strlen($title_trim) > 60) {
			$title_trim = substr($title_trim,0,60);
		} ?>

		<li><a href="<?php the_permalink(); ?>"><?php echo $title_trim; ?></a></li>
	<?php endwhile;
	rewind_posts();
	echo '</ul>';
}

..and then display the list wherever in your theme using this template tag:

<?php popular_posts_per_category(); ?>

There’s a lot going on in the code above, so here’s the scoop if you’re into understanding what you’re doing:

  1. First we create an array of unique categories
  2. Then we set up our arguments for the new WP_Query()
  3. Finally the results are truncated and displayed as a nice list

Note there are several ways to customize this technique. You can customize the query itself, for example, to get more popular posts by editing the posts_per_page parameter. Another good thing to customize is the substr() action that shortens the length of the post title — super-useful when displaying the popular-post list in smallish places.

And lastly, you can use additional parameters to further refine the popular-post query. For example, I want to exclude posts from certain tags from appearing on the list, so I include the following argument in the array:

'tag__not_in' => array(38,81,390),

..where 38, 81, and 390 are IDs of the tags I want to omit. WordPress provides a cornucopia of other parameters to help with further fine-tuning of your custom queries.

Allow markup in category descriptions

This next trick does one thing and does it well: allows markup in your category descriptions. By default, HTML markup is stripped from category descriptions, but thanks to this thread, the following code makes it possible:

// markup in category descriptions
$filters = array('pre_term_description', 'pre_link_description', 'pre_link_notes', 'pre_user_description');
foreach ($filters as $filter) {
	remove_filter($filter, 'wp_filter_kses');
}

Bada-boom, bada-bing — no editing required, just drop into your functions.php file and you’re good to go.

Add extra fields to category meta

Lastly, we have a technique for adding an extra fields to the category metadata. In my case, I wanted to include category meta boxes for a custom title and a category thumbnail image. After some searching and fiddling, I found this method works a treat for making it happen. Add the following code to your functions.php file:

// add extra fields to category meta
define('MY_CATEGORY_FIELDS', 'my_category_fields_option');
add_filter('edit_category_form', 'my_category_fields');
function my_category_fields($tag) {
	$tag_extra_fields = get_option(MY_CATEGORY_FIELDS); ?>
	<table class="form-table">
		<tr class="form-field">
			<th scope="row" valign="top"><label for="custom_cat_title">Custom Category Title</label></th>
			<td><textarea  name="custom_cat_title" id="custom_cat_title"><?php echo $tag_extra_fields[$tag->term_id]['my_description']; ?></textarea>
				<p class="description">Custom category title attribute for menus on parent pages</p></td>
		</tr>
		<tr>
			<th scope="row" valign="top"><label for="custom_cat_thumb">Custom Category Thumbnail</label></th>
			<td><input name="custom_cat_thumb" type="text" id="custom_cat_thumb" size="50" aria-required="false" value="<?php echo $tag_extra_fields[$tag->term_id]['my_thumbnail']; ?>" />
				<p class="description">Upload the image and enter the filename here.</p></td>
		</tr>
	</table>
    <?php
}
add_filter('edited_terms', 'update_my_category_fields');
function update_my_category_fields($term_id) {
  if($_POST['taxonomy'] == 'category'):
    $tag_extra_fields = get_option(MY_CATEGORY_FIELDS);
    $tag_extra_fields[$term_id]['my_description'] = strip_tags($_POST['custom_cat_title']);
    $tag_extra_fields[$term_id]['my_thumbnail'] = strip_tags($_POST['custom_cat_thumb']);
    update_option(MY_CATEGORY_FIELDS, $tag_extra_fields);
  endif;
}
add_filter('deleted_term_taxonomy', 'remove_my_category_fields');
function remove_my_category_fields($term_id) {
  if($_POST['taxonomy'] == 'category'):
    $tag_extra_fields = get_option(MY_CATEGORY_FIELDS);
    unset($tag_extra_fields[$term_id]);
    update_option(MY_CATEGORY_FIELDS, $tag_extra_fields);
  endif;
}

Once added to your functions file, this code will generate two custom meta fields in the Edit Category page in the WP Admin. The first field is for a custom title, and the second is for a custom category image. No editing is required to make this work, but the application is rather project-specific, so you’ll probably want to customize things by adding/removing fields, modifying markup, and so on to suit your specific needs.

Wrap up

Categories arguably are the primary vehicle for content organization. Yet sadly, most sites do very little to emphasize and promote their best content in ways that are contextually relevant and easy to navigate. In this post, I outline some effective ways to maximize your WordPress category archives using a variety of choice, custom-crafted code snippets. No more excuses for boring category archives people!