WordPress – Dynamically injecting menu items using Walker Class

I am trying to pull in some dynamic (non-WP) content to populate a menu in WordPress. For this I’ve extended the Walker Class as shown here:

http://www.kriesi.at/archives/improve-your-wordpress-navigation-menu-output

Read More

So my Menu would look like

  • Home
  • Blog
  • News
  • Features
  • competitions –
    • Comp1
    • Comp2

Where Comp1 and Comp2 are pulled from a DB on a seperate site. They are just links to an external site, so the only relevant value is the ‘Comp Title’ and ‘Comp URL’

The main method within my class is :

 function start_el(&$output, $item, $depth, $args)
    {

        global $wp_query;
        $indent = ( $depth ) ? str_repeat( "t", $depth ) : '';

        $class_names = $value = '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;

        $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
        $class_names = ' class="'. esc_attr( $class_names ) . '"';

        $output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';

        $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
        $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
        $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
        $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';

        $prepend = '<strong>';
        $append = '</strong>';
        $description  = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';

        if($depth != 0)
        {
            $description = $append = $prepend = "";
        }

        if($item->title == 'Competitions')
        {
            $item_output = $args->before;
            $item_output .= '<a'. $attributes .'>';
            $item_output .= $args->link_before .$prepend.apply_filters(  'the_title', $item->title, $item->ID ).$append;
            $item_output .= $description.$args->link_after;
            $item_output .= '</a>';
            $item_output .= $args->after;

            $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );

            $this->loopComps($output , $args);

        }
        else
        {
            $item_output = $args->before;
            $item_output .= '<a'. $attributes .'>';
            $item_output .= $args->link_before .$prepend.apply_filters( 'the_title', $item->title, $item->ID ).$append;
            $item_output .= $description.$args->link_after;
            $item_output .= '</a>';
            $item_output .= $args->after;

            $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
        }
    }

and then the loopComps method is:
(getGalleryComps() pulls in the dynamic content)

 function loopComps($output , $args)
    {
        $openComps = $this->getGalleryComps();
        foreach($openComps as $comp)
        {
            $item =  new StdClass;
            $item->ID = 9999;
            $item->post_author = 5;
            $item->post_date = '2012-11-16 10:48:44';
            $item->post_date_gmt = '2012-11-16 10:48:44';
            $item->title = $comp['competition_name'];
            $item->post_type = 'nav_menu_item';
            $item->post_name = $comp['competition_name'];
            $item->post_title = $comp['competition_name'];
            $item->post_excerpt = $comp['competition_name'];
            $item->guid = $comp['competition_name'];
            $item->url = 'http://www.mycomps';
            $item->post_status = 'publish';
            $item->post_parent = 0;
            $item->filter = 'raw';
            $item->menu_item_parent = '6845';
            $item->object_id = '99999';
            $item->object = 'custom';
            $item->type = 'custom';
            $item->classes = array(null , 'menu-item' , 'menu-item-type-custom' , 'menu-item-object-custom');
            $item->menu_order = 6;


            return $this->start_el($output, $item, 1, $args);


        }

    }

This all seems to work fine , expect the $item is never attached to the actual menu. If I print out the $items during the nav menu loop, I can see my dynamic content which I’ve created a ‘psuedo Post’ is there , it just never gets attached to the menu.

Is there an easier way of injecting menu items?

UPDATE 1:

I’ve adjusted loopComps method to be:

 function loopComps($output)
    {
        $openComps = $this->getGalleryComps();

        $output .= '<ul>';
        foreach( $openComps as $openComp )
        {
            $output .= '<li><a href='.$openComp->url.'>'.$openComp['competition_name]'].'</a></li>';
        }
        $output .= '</ul>';

        return $output;
  }

As this does make much more sense , but doesn’t solve the issue, the data is there, but is never shown in the menu.

Related posts

Leave a Reply

3 comments

  1. I’m not sure if I understood your question properly but since you asked Is there an easier way of injecting menu items?, so I think you can add extra menu items easily using wp_nav_menu_items hook like

    add_filter( 'wp_nav_menu_items', 'your_custom_menu_item', 10, 2 );
    function your_custom_menu_item ( $items, $args ) {
        // First get the data from your database and replace menu titles and links
        // then loop the result and add items
        $myMenu='<li><a href="#">External Links</a><ul>'; // parent
        $myMenu.='<li><a target="_blank" href="http://facebook.com">Facebook</a></li>';
        $myMenu.='<li><a target="_blank" href="http://google.com">Google</a></li></ul></li>';
        $items .= $myMenu;
        return $items;
    }
    

    Just paste the code in your functions.php and replace your menu title and link with yours.

  2. You should just have to append the HTML for the menu items for the competitions to $output, e.g.,

    $output .= '<ul>';
    foreach( $comps as $comp ) {
        $output .= '<li><a href='.$comp->url.'>.$comp->title.'</a></li>';
    }
    $output .= '</ul>';
    

    That’s why $output is passed by reference (‘&’) to start_el().

    This example is probably simplistic, because you’ll have to adjust it to whatever prototype your WP-generated menu items follow, but it illustrates the principle. Creating mock WordPress items and trying to trick WordPress into doing the work for you, doesn’t seem like the right approach.

  3. After review of question and answer i have added ‘GOOGLE’ sub menu item under the ‘Competitions’ parent menu so here is complete solution for editing menu bar.

    • 1

      First put this code where menu bar is called mostly it should be in header.php and change your custom menu which i have mentioned in comment

      class description_walker extends Walker_Nav_Menu
      {
        function start_el(&$output, $item, $depth, $args){
          global $wp_query;
          $indent = ( $depth ) ? str_repeat( "t", $depth ) : '';
          $class_names = $value = '';
          $classes = empty( $item->classes ) ? array() : (array) $item->classes;
          $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
          $class_names = ' class="'. esc_attr( $class_names ) . '"';
          $output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';
          $attributes  = ! empty( $item->attr_title ) ? ' title="'  . esc_attr( $item->attr_title ) .'"' : '';
          $attributes .= ! empty( $item->target )     ? ' target="' . esc_attr( $item->target     ) .'"' : '';
          $attributes .= ! empty( $item->xfn )        ? ' rel="'    . esc_attr( $item->xfn        ) .'"' : '';
          $attributes .= ! empty( $item->url )        ? ' href="'   . esc_attr( $item->url        ) .'"' : '';
          $prepend = '<strong>';
          $append = '</strong>';
          $description  = ! empty( $item->description ) ? '<span>'.esc_attr( $item->description ).'</span>' : '';
          if($depth != 0){
           $description = $append = $prepend = "";
          }
          if($item->title=='Competitions'){
            $item_output = $args->before;
            $item_output .= '<a'. $attributes .'>';
            $item_output .= $args->link_before .$prepend.apply_filters(  'the_title', $item->title, $item->ID ).$append;
            $item_output .= $description.$args->link_after;
            $item_output .= '</a>';
            $item_output .= $args->after;
            /* YOUR CUSTOM MENU OR SUBMENU -START- */
            $output .= '<ul class="sub-menu">';
            $output .= '<li><a href='.'http://www.google.com'.'>'.'GOOGLE'.'</a></li></ul>';
            /* YOUR CUSTOM MENU OR SUBMENU -END- */
            $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
          }else{
            $item_output = $args->before;
            $item_output .= '<a'. $attributes .'>';
            $item_output .= $args->link_before .$prepend.apply_filters( 'the_title', $item->title, $item->ID ).$append;
            $item_output .= $description.$args->link_after;
            $item_output .= '</a>';
            $item_output .= $args->after;
            $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
          }
        }
      }
      
      • 2
        call the wp_nav_menu and pass custom walker object which we have just created.

      wp_nav_menu( array(
      'container' =>false,
      'menu_class' => 'menuPrincipal',
      'echo' => true,
      'before' => '',
      'after' => '',
      'link_before' => '',
      'link_after' => '',
      'depth' => 0,
      'walker' => new description_walker())
      );

    it will add GOOGLE sub menu under Competitions menu

    First answer is correct but the class member output could not set by loopComps and also we should not return output just to set output member so we just set direct sub menu to the walker.

    It works fine.