How to dynamically add custom taxonomy terms as a sub-menu of an existing menu item, using custom walker class

wordpress.stackexchange https://wordpress.stackexchange.com/questions/354806

Вопрос

Desired outcome

I have a custom post type and belonging to it, a custom taxonomy. I'm trying to get my child theme dynamically add the terms belonging to that taxonomy as a sub-menu under a certain list item in one of my theme's menus. The taxonomy is hierarchical, and I would like to add all terms of depth = 1, i.e. the first and second-level terms with proper indentation and the same styles as my current menu.

My strategy

So I first thought of using the wp_nav_menu_items filter, but since there can be many terms and they are hierarchical, I instead thought it's best to go with a custom walker to extend the Walker_Nav_Menu class.

The issue

The issue I've encountered is that when I try to call the get_terms() function inside the custom walker, it returns "Invalid taxonomy". I know for a fact that the taxonomy is registered and that there are terms in the database. It seems like the get_terms() is getting called before init when it's invoked in the walker class. Since the taxonomy gets registered on init, the taxonomy is invalid when the walker class is calling get_terms();

What I've tried

So I tried to lift out the get_terms() function from the class and do the following:

function get_mytax_terms() {
  $myterms= get_terms( array(
    'taxonomy' => 'mytax',
    'hide_empty' => true
  ) );
  return $myterms;
}
add_action( 'init' , get_mytax_terms() , 10 );

I even tried making the variable global, but it doesn't return any terms, and throws an error for the taxonomy being invalid. A var_dump still gives me "Invalid taxonomy" error. I read on, and saw in a comment at the codex (on the Walker_Nav_menu class page) that I need to use static functions, and did this inside the custom walker class:

/**
   * Get all terms of tax mytax
   */
  public static function init() {
    add_action( 'init', array( get_called_class(), 'get_mytax_terms' ), 10 );
  }
  public static function get_mytax_terms() {
    $myterms = get_terms( array(
      'taxonomy' => 'mytax',
      'hide_empty' => true
    ) );
    return $myterms ;
    var_dump($myterms );
  }

That var_dump runs but still returns the "Invalid taxonomy" error.

Edit

I tried changing my custom taxonomy to the built-in taxonomy 'category' and then my function works. Using 'mytax' it still gives me invalid taxonomy.

The custom taxonomy is registered with

add_action( 'init', 'custom_register_taxonomies');

I am now fairly certain that this issue is due to the wp_nav_menu call coming earlier than the registration of my custom taxonomy.

The questions

Is it safe to register the custom taxonomies earlier than on init?

Am I right in using static functions in the custom walker class, as described above?

Would it improve anything to delay the wp_nav_menu call, by calling it from a custom location in my child theme. Rather than letting the parent theme call it, and adapting my walker class and/or taxonomy registration function?

Am I on the right track here or am I missing something else?

Current situation

I'm including my custom walker below. Any tips are greatly appreciated.

/* ---- Add mytax taxonomy terms to menu ---- */
class Walker_Add_Myterms extends Walker_Nav_Menu {
  // Insert a submenu
  function end_el( &$output, $item, $depth=1, $args=array() ) {
      // if the current menu item being output is parentmenuitem
      if( 'parentmenuitem' == $item->title ){
          // get all terms
          $myterms = get_terms( array(
              'taxonomy' => 'mytax',
              'hide_empty' => true,
          ) );
          if ( ! empty( $myterms ) && ! is_wp_error( $myterms ) ) {
              // start a new list
              $output .= '<ul>';
              // iterate over each type and add an li
              foreach( $myterms $myterm){
                  $term_url = get_term_link( $myterm->term_id , 'mytax');
                  $name = $myterm->name;
                  $format = '<li><a href="%s">%s</a></li>';
                  $output .= sprintf( $format, $term_url, $name );
              }
              // close the list
              $output .= '</ul>';
          }
      }
      // close the parent li
      $output .= "</li>\n";  
  }
}
wp_nav_menu( array(
    'theme_location'  => 'expanded',
    'container'       => 'ul',
    'menu_class'      => 'expanded-menu sub-menu active',
    'walker'          => new Walker_Add_Myterms
) );
Это было полезно?

Решение

Okay, I figured it out. It's not very smooth, but at least it works as I want it to. Will have to do some refactoring.

The issue was that wp_nav_menu() got called earlier than the taxonomy registration. I had accidentally placed it in my theme-functions.php and forgotten about it, then called it again in my header.php . That's why it seemed to work, but I still got the invalid taxonomy error and a broken menu.

Here's the final walker class:

class Walker_Add_Myterms extends Walker_Nav_Menu {
    public function end_el( &$output, $item, $depth=1, $args=array() ) {
    // if the current menu item being output has class "uthyrning"
    if( in_array( 'parentliitemclass' , $item->classes ) ){
        // get all terms
        $myterms = get_terms( array(
            'taxonomy'      => 'mytax',
            'hide_empty'    => true,
            'parent'        => 0,
            ) );
        if ( ! empty( $myterms ) && ! is_wp_error( $myterms ) ) {
            // Insert a submenu
            $output .= '<ul class="sub-menu">';
            // iterate over each type and add an li
            foreach( $myterms as $myterm){

                $term_url   = get_term_link( $myterm->term_id , 'mytax' );
                $name       = $myterm->name;
                $li_id      = 'id="term-item-' . $myterm->term_id . '"';
                $li_classes = 'menu-item menu-item-type-taxonomy menu-item-object-mytax term-item-' . $myterm->term_id;
                $wrap_class = 'class="ancestor-wrapper"';
                $format     = '<li %s class="%s"><div %s><a href="%s">%s</a></div></li>';

                // Get child terms ,and if they exist, create sub-menu with term children

                $children = get_term_children( $myterm->term_id, 'mytax' );
                if( isset($children) && ! is_wp_error( $children ) && sizeof($children) > 0) {
                    $li_classes .= ' menu-item-has-children';
                    $child_items = '';
                    foreach ( $children as $child ) {
                        $child_term = get_term( $child, 'mytax' );
                        $child_item     =   '<li id="term-item-' . $child_term->term_id . '" class="menu-item menu-item-type-taxonomy menu-item-object-mytax term-item-' . $child_term->term_id . '"><div class="ancestor-wrapper">';
                        $child_item     .=  '<a href="' . get_term_link( $child_term, 'mytax' ) . '">' . $child_term->name . '</a>';
                        $child_item     .=  '</div></li>';
                        $child_items    .=  $child_item;
                    }
                    $format         = '<li %s class="%s"><div %s><a href="%s">%s</a>';
                    $format         .= '<button class="toggle sub-menu-toggle fill-children-current-color" data-toggle-target=".menu-modal .term-item-' . $myterm->term_id . ' > .sub-menu" data-toggle-type="slidetoggle" data-toggle-duration="250" aria-expanded="false"><span class="screen-reader-text">Visa undermeny</span><svg class="svg-icon" aria-hidden="true" role="img" focusable="false" xmlns="http://www.w3.org/2000/svg" width="20" height="12" viewBox="0 0 20 12"><polygon fill="" fill-rule="evenodd" points="1319.899 365.778 1327.678 358 1329.799 360.121 1319.899 370.021 1310 360.121 1312.121 358" transform="translate(-1310 -358)"></polygon></svg></button>';
                    $format         .= '</div><ul class="sub-menu">';
                    $format         .= $child_items . '</ul></li>';
                }

                // END Child terms
                $output     .= sprintf( $format, $li_id, $li_classes, $wrap_class, $term_url, $name );
            }
            // close the list
            $output .= '</ul>';
        }
    }
    // close the parent li
    $output .= "</li>\n";  
    }
}

And I also ended up copying the template-part where the wp_nav_menu() is called, from the parent theme, and replacing the default walker_nav_menu class with my own extended class above.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с wordpress.stackexchange
scroll top