Extending the Category Walker

Categories are better than tags! Why? Because a category can have child categories, and grand-child categories and so on. You can do all sorts of interesting things with them.

Say you want to display only the categories related to a certain post, while maintaining the hierarchy.

You could try to painstakingly re-create the category tree from the bottom up and then display it somehow.

Or… you could choose to simply skip the categories you don’t want to have displayed. Makes sense? Read on.

Enter Walkers

A Walker is a special class for displaying various tree-like structures. WordPress uses them for displaying categories, pages and threaded comments.

The wp_list_categories() template tag uses one of them internally. It’s called Walker_Category. The nice bit is that you can replace the default walker with your own.

Walk Your Own Way

First we’ll need to make our own walker, using Walker_Category as a base:

class Post_Category_Walker extends Walker_Category {

	private $term_ids = array();

	function __construct( $post_id, $taxonomy )  {
		// fetch the list of term ids for the given post
		$this->term_ids = wp_get_post_terms( $post_id, $taxonomy, 'fields=ids' );
	}

	function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
		$display = false;

		$id = $element->term_id;

		if ( in_array( $id, $this->term_ids ) ) {
			// the current term is in the list
			$display = true;
		}
		elseif ( isset( $children_elements[ $id ] ) ) {
			// the current term has children
			foreach ( $children_elements[ $id ] as $child ) {
				if ( in_array( $child->term_id, $this->term_ids ) ) {
					// one of the term's children is in the list
					$display = true;
					// can stop searching now
					break;
				}
			}
		}

		if ( $display )
			parent::display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output );
	}
}

display_element() is a method in the Walker class. It’s called once for each element.

What happens here is that we first check if either the current element or one of its children is in the category list of the given post. If it is, we let Walker_Category do the actual displaying.

All that’s left to do now is pass our enhanced walker to wp_list_categories(). We’ll wrap it up in a function, for convenience:

function walk_post_categories( $post_id, $args = array() ) {
	$args = wp_parse_args( $args, array(
		'taxonomy' => 'category'
	) );

	$args['walker'] = new Post_Category_Walker( $post_id, $args['taxonomy'] );

	$output = wp_list_categories( $args );
	if ( $output )
		return $output;
}

And that’s about it. You now have a neat little template tag:

<?php walk_post_categories(123); ?>

You can even pass additional arguments to wp_list_categories:

<?php walk_post_categories(123, 'show_count=1&title_li='); ?>

Keep On Walking

Of course, this is just one example. You can use this approach with any type of walker. Let me know what other uses you’ve found.

comments powered by Disqus