1

I want to plain to cache a page menu, that takes about 50 queries on page load.

At the moment I have cache setting code with Transient API

$housemenu = get_transient('housemenu_key');
    if (!$housemenu) {
        $housemenu = wp_nav_menu(
            array(
                'menu' => '',
                'menu_class' => 'sf-menu',
                'menu_id' => 'nav',
                'walker' => new description_walker()
            )
        );
        echo 'not-set';
        set_transient(
            'housemenu_key',
            $housemenu,
            60*60*3
        );
    }
echo $housemenu;

$housemenu = get_transient('housemenu_key'); returns a nothing. If i set like this:

$housemenu = 'return corrent';
echo 'not-set';

All works perfect. Why this menu is not working? Menu can be a walker transient?

Use: Wordpress 3.8

3 Answers 3

3

wp_nav_menu() doesn't return anything by default, it just echoes out formatted html.

For wp_nav_menu() to return a value you will need to pass 'echo' => false into it's arguments, like so:

$housemenu = wp_nav_menu( array(
    'menu' => '',
    'menu_class' => 'sf-menu',
    'menu_id' => 'nav',
    'walker' => new description_walker(),
    'echo' => 'false'
));

It should be noted that by caching the entire menu in a transient, you will lose it dynamic nature. For instance if it cached it while on your home page all of the active classes applied to the menu items will ALWAYS reflect the home page. Thus if you were to visit a different page the active classes on the menu items would be wrong.

This will cause quite a bit of pain if you wish to convey via CSS which page the current user is on.

4
  • The same, dont' work with set_transient and get_transient
    – Foxsk8
    Jan 14, 2014 at 22:12
  • It does indeed work as I've just tested it out on my local. Is what you have above an exact copy of the code you are using? Does your wp_nav_menu() function generate a menu when 'echo' => true with the current settings?
    – user10106
    Jan 14, 2014 at 22:24
  • 2
    Do a one-time delete_transient('housemenu_key'); Then your menu will start working. The reason it is not working for you now is because you still have the old empty transient set from your previous attempts. Or just wait three hours for the transient to expire.
    – Otto
    Jan 14, 2014 at 22:38
  • "It should be noted that by caching the entire menu in a transient, you will lose it dynamic nature. " -- and not just stuff like CSS classes (current_item), but also the links themselves. Loading the site over HTTPS makes links start with HTTPS instead of HTTP; using a translation plugin like WPML can change the menu entirely, to translated pages; using a mobile domain with a mobile switcher plugin can change www. to m. or other mobile domain variant; etc.
    – webaware
    Jan 15, 2014 at 2:26
1

By default wp_nav_menu() echoes result and returns nothing. You need to set 'echo' => false in its arguments to flip the behavior and be able to assign result to a variable.

1
  • Not working. Menu shows up, but $housemenu = get_transient('housemenu_key'); is like a empty and then gos to function and each time menu is set over and over $housemenu = wp_nav_menu( array('menu' => '','menu_class' => 'sf-menu','menu_id' => 'nav','walker' => new description_walker(),'echo' => false));
    – Foxsk8
    Jan 14, 2014 at 21:56
1

one problem exists there... Wordpress has built-in page detection, to give the CSS classes to current page in menu... and if cached, then your navigation menu-items will never get "current-page" class or etc..

however, there are 2 solutions:

Solution 1

Before caching, once, add classes to <a> items:

function add_menu_atts( $atts, $item, $args ) { 
   $atts['class'] = $item->type.'-'.$item->ID;      return $atts;  
}
add_filter( 'nav_menu_link_attributes', 'add_menu_atts', 8, 3 );

and then cache the menu... it will have such output:

<ul class="..." id="...">
<li class="........"><a class="post_type-312" href="......">Post titleee (it's id is 312)</a></li>
<li class="........"><a class="taxonomy-42" href="....">Category titleee (it's id is 42)</a></li>
</ul>

so, attach "current-item" class from javascript (in page body you should pass variable to javascript, to find out if you are on a post,page,category or etc..)... i.e.:

<script>
var CurrentPageType="post_type";  //or "taxonomy"(for categories); or author or etc...
var CurrentItemId=612;
var element = document.getElementsByClassName(CurrentPageType +'-'+ CurrentItemId)[0]; element.className +=" current-menu-item";
</script>

solution 2

for typical,non wp-sites. i have tried to code a JS script, which may solve the problem.. So, in case you cache the menu, and want to get "current-menu-item" class, then put this function in your page source/cache (better to be in the end of body):

<script> Highlight_Current_Menu_link("current_item__from_JS", "parent_div_id" ); </script> 
or
<script> Highlight_Current_Menu_link("current_item__from_JS"); </script> 

at first, add this code in header(better inside .js file):

<script>
function Highlight_Current_Menu_link(Added_class_name, Ancestor_to_search_in, link_to_find){
    var Added_class_name = Added_class_name || "current_item__from_JS";  
    var link_to_find = link_to_find || window.location.pathname;
    var Ancestor_to_search_in = typeof Ancestor_to_search_in === "undefined" ? document.body : getElementById(Ancestor_to_search_in);
    var A_elements = Ancestor_to_search_in.getElementsByTagName("a");
    for(var i=0; i<A_elements.length; i++){
        var condition=false;
        var href=A_elements[i].href.replace(window.location.origin,""); 
        if(href.indexOf('#')>-1 && href==link_to_find)  {condition=true;}
        else if( RemoveHashString(link_to_find).indexOf(RemoveHashString(href)) >- 1 && 
            href.substring(0, href.lastIndexOf("/")).trim() == link_to_find.substring(0, link_to_find.lastIndexOf("/")).trim()
            ) {
             condition= true;
        }
        if (condition) { A_elements[i].className += " "+Added_class_name; }
    }
}
        function RemoveHashString(str){  var hash=str.split("#")[1];  return str.replace("#"+hash,'');  }
</script>

and determine your desired style to the attached class

<style>
.current_item__from_JS{background:red;}
</style>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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