3

I'm trying to replace the walker argument from new My_Walker_Nav_Menu() to a string 'My_Walker_Nav_Menu' in this wp_nav_menu call (which works as is)

wp_nav_menu(
     array(
           'theme_location'    => 'header_nav',
           'menu_class'             => 'main-menu',
           'container'         => '',
           'fallback_cb'       => false,
           'walker' => new My_Walker_Nav_Menu()
           )
     );

When I change it to a string I'm getting this error Fatal error: Using $this when not in object context

The class is basic:

class My_Walker_Nav_Menu extends Walker_Nav_Menu {
    function start_lvl(&$output, $depth = 0, $args = Array()) {
        $indent = str_repeat("\t", $depth);
        if('header_nav' == $args->theme_location ){
            $output .='<span class="toggle-submenu fa fa-angle-down"></span>';
        }
        $output .= "\n$indent<ul class=\"sub-menu\">\n";
    }
}

I need to do this because apparently Customizer's partial refresh doesn't work with custom walkers.

When the Customizer determines it can’t do a partial refresh, it falls back to performing a full page refresh. Conditions for why a menu change will get a full page refresh include:

...

  • or if wp_nav_menu() is called with with a walker object instance, as opposed to a class name string;

Somebody else had this issue (mentioned in a comment) but didn't get an answer. I've searched through the docs but can't seem to find anything.

Any help is much appreciated.

  • Hm, in testing with my own theme and looking at the source of wp_nav_menu(), and walk_nav_menu_tree() that it uses, I don't see how passing a class name string can even work. Walker_Nav_Menu extends Walker, which is riddled with $this, so cannot be called statically. As far as I can tell, the only way you could use a class name string as the walker argument for wp_nav_menu is if the class did not extend Walker_Nav_Menu or Walker and instead re-implemented all its methods statically. – Jacob Peattie Feb 28 '18 at 15:54
  • It's worth noting that the documentation for wp_nav_menu still only says that walker is for an "Instance of a custom walker class. Default empty." Key word there being "instance". – Jacob Peattie Feb 28 '18 at 15:56
4

Apparently custom nav menu walkers are not supported though they were supposed to be. I'm surprised this only has been discovered now. However, there is an easy fix for you to get them to work. Just add this to your theme:

add_filter( 'wp_nav_menu_args', function( $args ) {
    if ( isset( $args['walker'] ) && is_string( $args['walker'] ) && class_exists( $args['walker'] ) ) {
        $args['walker'] = new $args['walker'];
    }
    return $args;
}, 1001 ); // 1001 because \WP_Customize_Nav_Menus::filter_wp_nav_menu_args() runs at 1000.

The following core patch would eliminate the need for this filter:

--- src/wp-includes/nav-menu-template.php
+++ src/wp-includes/nav-menu-template.php
@@ -526,6 +526,9 @@ function _wp_menu_item_classes_by_context( &$menu_items ) {
  */
 function walk_nav_menu_tree( $items, $depth, $r ) {
    $walker = ( empty($r->walker) ) ? new Walker_Nav_Menu : $r->walker;
+   if ( is_string( $walker ) && class_exists( $walker ) ) {
+       $walker = new $walker();
+   }
    $args = array( $items, $depth, $r );

    return call_user_func_array( array( $walker, 'walk' ), $args );
  • This core patch looks like a good addition. PS: I could only find this many years old ticket #14142, that seems to report this behavior (see you've reopen it some time ago) – birgire Feb 28 '18 at 18:17
  • This solution works in combination with this solution. wordpress.stackexchange.com/a/281947/24599 And you should not change your Custom Walker either. – Syed Apr 19 at 18:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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