Caching WordPress navigation menus – wp_nav_menu() wrapper

Navigation menus in WordPress are a great thing, sadly they are usually not performing very well and you might want to cache them. Here’s a small set of functions that allow you to cache the output of the navigation menus. Please note the inline comments in case your navigation menus have different layouts depending on the posts or categories.

Once you throw this in your themes’ functions.php or a file included from there you can use hh_cached_nav_menu() as replacement for wp_nav_menu().

<?php
/**
 * Wrapper function around wp_nav_menu() that will cache the wp_nav_menu for all tag/category
 * pages used in the nav menus
 * @see http://lookup.hitchhackerguide.com/wp_nav_menu for $args
 * @author tott
 */ 
function hh_cached_nav_menu( $args = array(), $prime_cache = false ) {
	global $wp_query;
	
	$queried_object_id = empty( $wp_query->queried_object_id ) ? 0 : (int) $wp_query->queried_object_id;
	
	// If design of navigation menus differs per queried object use the key below
	// $nav_menu_key = md5( serialize( $args ) . '-' . serialize( get_queried_object() ) );
	
	// Otherwise
	$nav_menu_key = md5( serialize( $args ) );
	
	$my_args = wp_parse_args( $args );
	$my_args = apply_filters( 'wp_nav_menu_args', $my_args );
	$my_args = (object) $my_args;
	
	if ( ( isset( $my_args->echo ) && true === $my_args->echo ) || !isset( $my_args->echo ) ) {
		$echo = true;
	} else {
		$echo = false;
	}
	
	$skip_cache = false;
	$use_cache = ( true === $prime_cache ) ? false : true;
	
	// If design of navigation menus differs per queried object comment out this section
	//*
	if ( is_singular() ) {
		$skip_cache = true;
	} else if ( !in_array( $queried_object_id, hh_get_nav_menu_cache_objects( $use_cache ) ) ) {
		$skip_cache = true;
	}
	//*/
	
	if ( true === $skip_cache || true === $prime_cache || false === ( $nav_menu = get_transient( $nav_menu_key ) ) ) {
		if ( false === $echo ) {
			$nav_menu = wp_nav_menu( $args );
		} else {
			ob_start();
			wp_nav_menu( $args );
			$nav_menu = ob_get_clean();
		}
		if ( false === $skip_cache )
			set_transient( $nav_menu_key, $nav_menu );
	} 
	if ( true === $echo )
		echo $nav_menu;
	else
		return $nav_menu;
}

/**
 * Invalidate navigation menu when an update occurs
 */
function hh_update_nav_menu_objects( $menu_id = null, $menu_data = null ) {
	hh_cached_nav_menu( array( 'echo' => false ), $prime_cache = true );
}
add_action( 'wp_update_nav_menu', 'hh_update_nav_menu_objects' );

/** 
 * Helper function that returns the object_ids we'd like to cache
 */
function hh_get_nav_menu_cache_objects( $use_cache = true ) {
	$object_ids = get_transient( 'hh_nav_menu_cache_object_ids' );
	if ( true === $use_cache && !empty( $object_ids ) ) {
		return $object_ids;
	}

	$object_ids = $objects = array();
	
	$menus = wp_get_nav_menus();
	foreach ( $menus as $menu_maybe ) {
		if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id ) ) {
			foreach( $menu_items as $menu_item ) {
				if ( preg_match( "#.*/category/([^/]+)/?$#", $menu_item->url, $match ) )
					$objects['category'][] = $match[1];
				if ( preg_match( "#.*/tag/([^/]+)/?$#", $menu_item->url, $match ) )
					$objects['post_tag'][] = $match[1];
			}
		}
	}
	if ( !empty( $objects ) ) {
		foreach( $objects as $taxonomy => $term_names ) {
			foreach( $term_names as $term_name ) {
				$term = get_term_by( 'slug', $term_name, $taxonomy );
				if ( $term )
					$object_ids[] = $term->term_id;
			}
		}
	}
	
	$object_ids[] = 0; // that's for the homepage
	
	set_transient( 'hh_nav_menu_cache_object_ids', $object_ids );
	return $object_ids;
}

Trackbacks/Pingbacks

  1. WordPress Transients API – Practical examples | CatsWhoCode.com - January 23, 2012

    […] → Source: https://hitchhackerguide.com/2011/10/07/caching-wordpress-navigation-menus-wp_nav_menu-wrapper/ […]

  2. WordPress Transients API – Practical examples « The Blog of Jordan Rynard - February 7, 2012

    […] → Source: https://hitchhackerguide.com/2011/10/07/caching-wordpress-navigation-menus-wp_nav_menu-wrapper/ […]

  3. WordPress Transients API – Practical examples | PHP Developer Resource - April 9, 2012

    […] → Source: https://hitchhackerguide.com/2011/10/07/caching-wordpress-navigation-menus-wp_nav_menu-wrapper/ […]

  4. WordPress Transients API – Practical examples | Easy jQuery - April 10, 2012

    […] → Source: https://hitchhackerguide.com/2011/10/07/caching-wordpress-navigation-menus-wp_nav_menu-wrapper/ […]

  5. WordPress Transients API – Practical examples | عکس مدل لباس مانتو - April 19, 2012

    […] → Source: https://hitchhackerguide.com/2011/10/07/caching-wordpress-navigation-menus-wp_nav_menu-wrapper/ […]

  6. WordPress Transients API – Practical examples | web design resources | freebies | tutorials - May 5, 2012

    […] → Source: https://hitchhackerguide.com/2011/10/07/caching-wordpress-navigation-menus-wp_nav_menu-wrapper/ […]

  7. Errata and Clarifications for My WordCamp Seattle Talk, “There’s a Function for That: Doing More for Less with Core Functions” | tollmanz.com - May 21, 2012

    […] caching function by Thorsten Ott. I suggested that the code I presented (an abridged version of Thorsten’s code) used get_queried_object_id to get a unique ID for a nav menu. Unfortunately, because WordPress […]

Leave a comment