Create a custom walker class to extend wp_list_pages

The Walker class in WordPress is used to create hierarchical navigation links. It works by ‘walking’ — or iterating — over the data in the database and then outputting the results according to the parameters that have been set. The nice thing is that by using inheritance the class can be extended and manipulated in a child theme to output in a way that might be more suitable to the project at hand.

In this example the project required the featured image thumbnail to display in the menu for every page, so rather than displaying a list of links to pages, there is a menu of images. There are a number of ways to accomplish this on the front end, but manipulating the Walker class and creating a new function to display the menu is a solid backend approach.

If you examine the Walker class itself (wp-include/class-wp-walker.php) you will see that it is an abstract class that is mostly concerned with the hierarchical display. In this case we are only concerned with the output, so there is nothing for us to do directly with the Walker class. Instead, inside the post-template.php file (wp-includes/post-template.php) you will find a class called Walker_page, which extends the Walker class and creates the html output of displaying a list of pages.

Inside the Walker_page class is a function called start_el, which is where the actual html output occurs. There you will find this line:

$output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';

(you may need to highlight the line to see the whole thing)

It is easy to see from this how each link is constructed. So in order to manipulate the output, we need to alter the structure of this markup. We can do this in a child theme by creating a new class that extends the Walker_page class, just as Walker_page extends the Walker class. The beauty of object oriented programming is that classes can be extended and manipulated, while still keeping the main functionality of the class. So in this case, all we need to do is declare a new class and create a new start_el function that will mimic what happens in wp_list_pages. We do this in the functions.php file of our theme:

class Thumbnail_walker extends Walker_page {
// functions go here
}

We have now created a new class that inherits the properties of the Walker_page class. In this case we are essentially copying Walker_page, and just changing a bit of the output, so we copy the start_el function from Walker_page, and add get_the_post_thumbnail to it:

class Thumbnail_walker extends Walker_page {
    	function start_el(&$output, $page, $depth, $args, $current_page) {
		if ( $depth )
			$indent = str_repeat("\t", $depth);
		else
			$indent = '';

		extract($args, EXTR_SKIP);
		$css_class = array('page_item', 'page-item-'.$page->ID);
		if ( !empty($current_page) ) {
			$_current_page = get_page( $current_page );
			_get_post_ancestors($_current_page);
			if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )
				$css_class[] = 'current_page_ancestor';
			if ( $page->ID == $current_page )
				$css_class[] = 'current_page_item';
			elseif ( $_current_page && $page->ID == $_current_page->post_parent )
				$css_class[] = 'current_page_parent';
		} elseif ( $page->ID == get_option('page_for_posts') ) {
			$css_class[] = 'current_page_parent';
		}

		$css_class = implode( ' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page ) );

		$output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '">' . $link_before . apply_filters( 'the_title', '' ) . $link_after . get_the_post_thumbnail($page->ID, array(72,72)) .'</a>';

		if ( !empty($show_date) ) {
			if ( 'modified' == $show_date )
				$time = $page->post_modified;
			else
				$time = $page->post_date;

			$output .= " " . mysql2date($date_format, $time);
		}
	}
}

The thumbnail is now being called into each list item, and given a size of 72×72 pixels. We also removed the page title by calling an empty variable for ‘the_title’, so that only the thumbnail will show in the navigation. Further styling is going to be needed to make this look good, but this illustrates how to bring in the thumbnail into the list.

Now, in order to actually display our list, we need to instantiate the class so that the new code is called. We do this by calling ‘new’ and the name of the class, and by adding our new class to the parameters of wp_list_pages. You would place this code right in your theme template somewhere:

$thumb_menu = new Thumbnail_walker();
wp_list_pages(array('walker' => $thumb_menu));

Now our page list menu will pull in the featured image thumbnail for every post and display it. Keep in mind that there is no styling here, but you have the option of adding css classes into your new php class, or styling it afterwards.

But to take this even further, and make it more flexible, we can build a function to instantiate the class and show our new custom page menu.

Back in post_template.php is where the wp_list_pages function resides. Once again we copy the function and paste it into our child theme’s functions file. Now we can rename the function to one that relates to the new class we have made:

function thumbnail_pages($args = '') {
		$defaults = array(
		'depth' => 0, 'show_date' => '',
		'date_format' => get_option('date_format'),
		'child_of' => 0, 'exclude' => '',
		'title_li' => __('Pages'), 'echo' => 1,
		'authors' => '', 'sort_column' => 'menu_order, post_title',
		'link_before' => '', 'link_after' => '', 'walker' => '',
	);

	$r = wp_parse_args( $args, $defaults );
	extract( $r, EXTR_SKIP );

	$output = '';
	$current_page = 0;

	// sanitize, mostly to keep spaces out
	$r['exclude'] = preg_replace('/[^0-9,]/', '', $r['exclude']);

	// Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array)
	$exclude_array = ( $r['exclude'] ) ? explode(',', $r['exclude']) : array();
	$r['exclude'] = implode( ',', apply_filters('wp_list_pages_excludes', $exclude_array) );

	// Query pages.
	$r['hierarchical'] = 0;
	$pages = get_pages($r);

	if ( !empty($pages) ) {
		if ( $r['title_li'] )
			$output .= '<li class="pagenav">' . $r['title_li'] . '<ul>';

		global $wp_query;
		if ( is_page() || is_attachment() || $wp_query->is_posts_page )
			$current_page = $wp_query->get_queried_object_id();
		$output .= walk_page_tree($pages, $r['depth'], $current_page, $r);

		if ( $r['title_li'] )
			$output .= '</ul></li>';
	}

	$output = apply_filters('wp_list_pages', $output, $r);

	if ( $r['echo'] )
		echo $output;
	else
		return $output;
}

Once again we have copied the function directly and only changed the name, so that it maintains all the functionality of wp_list_pages.

At this point calling the function thumbnail_pages() won’t do anything, since we have not instantiated the class nor told the function which walker to use. You will notice that walker is included in the $defaults array, but with an emtpy string. So what we need to do is instantiate the class and use our new object as the parameter of the walker:

	$thumbmenu = new Thumbnail_walker();
	$defaults = array(
		'depth' => 0, 'show_date' => '',
		'date_format' => get_option('date_format'),
		'child_of' => 0, 'exclude' => '',
		'title_li' => __('Pages'), 'echo' => 1,
		'authors' => '', 'sort_column' => 'menu_order, post_title',
		'link_before' => '', 'link_after' => '', 'walker' => $thumbmenu,
	);

The $thumbmenu variable contains our class object, and it is placed in the walker to let WordPress know what it is looking for. Now we can call this modification in our theme by using the function thumbnail_pages().

As I said at the beginning there are a number of approaches that can get this result. But what’s cool about manipulating the core classes in WordPress is that you are using the full advantage of object oriented programming, and this makes it much easier to port the code to plugins, shortcodes, etc. And by manipulating the output markup only, you can keep content and styles separated.

13 comments

  1. Nicoletta

    Thank you so much! It took me hours to find out how to add informations from a post_meta key into wp_list_pages, or in better words: I failed until I read your brilliant article

  2. sherwin

    thanks for the article but i’m getting a

    Fatal error: Using $this when not in object context in class-wp-walker.php on line 185

    i still get the error even if i just copied the start_el() in my class. any ideas?

  3. Pingback: my first encounter with walkers | Rants, grunts and chants
Add Comment Register



Post a comment

You may use the following HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>