为 wordpress 写一个自定义的 nav walker

Writing a custom nav walker for wordpress

我正在尝试为我的自定义 wordpress 主题编写自定义导航菜单。

<ul class="MainMenu nav navbar-nav ">
<li class="has-megamenu">
    <a href="#">MENU</a>
    <div class="megamenu">
        <ul class="container megamenu-background">
            <li class="col-md-3 megamenu-column">
                <h4>Column 1</h4>
                <ul class="main-menu-ul">
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                </ul>
            </li>
            <li class="col-md-3 megamenu-column">
                <h4>Column 2</h4>
                <ul class="main-menu-ul">
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                </ul>
            </li>
            <li class="col-md-3 megamenu-column">
                <h4>Column 3</h4>
                <ul class="main-menu-ul">
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                </ul>
            </li>
            <li class="col-md-3 megamenu-column">
                <h4>Column 4</h4>
                <ul class="main-menu-ul">
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                    <li><a href="#">Menu Item</a></li>
                </ul>
            </li>
       </ul>
    </div>
</li>
<li class="dropdown"><a href="#">Contact</a></li>
<li class="dropdown"><a href="#">Our Story</a></li>

问题是如何编写这个结构walker

与其在 WordPress 管理员中将其构建为一个大菜单,我建议为 megamenu 中的每一列构建一个单独的菜单,然后再为联系人/关于/帐户链接构建一个菜单。我这样做的原因是因为您为每个 megamenu 列包含一个列标题,而 WordPress 管理菜单 UI 不包含向菜单中添加 non-link 项目的方法的盒子。添加 "Column Title" 内容 使其可编辑的最简单方法是制作单独的菜单,并使用菜单标题作为列标题。

对于functions.php:

<?php

// Register the menus
add_action( 'after_setup_theme', 'register_menus' );
function register_menus() {
    register_nav_menu( 'megamenu1', __( 'Megamenu Column 1', 'theme-text-domain' ) );
    register_nav_menu( 'megamenu2', __( 'Megamenu Column 2', 'theme-text-domain' ) );
    register_nav_menu( 'megamenu3', __( 'Megamenu Column 3', 'theme-text-domain' ) );
    register_nav_menu( 'megamenu4', __( 'Megamenu Column 4', 'theme-text-domain' ) );
    register_nav_menu( 'support', __( 'Support Menu', 'theme-text-domain' ) );
}

// Add the menu title
add_filter( 'wp_nav_menu_items', [ $this, 'include_menu_title' ], 10, 2 );
function include_menu_title( $nav_items, $args ) {

    if ( ! isset( $args->show_title ) || false === $args->show_title ) {
        return $nav_items;
    }

    if ( $args->theme_location && ( $locations = get_nav_menu_locations() ) && isset( $locations[ $args->theme_location ] ) ) {
        $menu = wp_get_nav_menu_object( $locations[ $args->theme_location ] );
        // Add the menu title. We add the UL element manually, because otherwise it would wrap AROUND the H4 later in execution of the wp_nav_menu function.
        $nav_items = '<h4>' . $menu->name  . '</h4><ul class="' . $args->menu_class . '">' . $nav_items . '</ul>';
    }

    return $nav_items;
}

// Add the dropdown class to support menu items.
add_filter('nav_menu_css_class', function($classes, $item, $args) {
    if ( 'support' !== $args->theme_location ) {
        return $classes;
    }
    $classes[] = 'dropdown';

    return $classes;
}, 10, 3);

然后 header.php:

<ul class="MainMenu nav navbar-nav">
    <li class="has-megamenu">
        <a href="#">Megamenu</a>
        <div class="megamenu">
            <div class="container megamenu-background">
                <?php
                $common_args = [
                    'show_title'      => true, // This is a custom arg used to indicate we want the title included.
                    'items_wrap'      => '%3$s', // Remove the UL element. We'll add it back at the right place in the filter.
                    'container_class' => 'col-md-3 megamenu-column',
                    'menu_class'      => 'vpm-menu-ul',
                ];
                wp_nav_menu( array_merge( ['theme_location'  => 'megamenu1'], $common_args ) );
                wp_nav_menu( array_merge( ['theme_location'  => 'megamenu2'], $common_args ) );
                wp_nav_menu( array_merge( ['theme_location'  => 'megamenu3'], $common_args ) );
                wp_nav_menu( array_merge( ['theme_location'  => 'megamenu4'], $common_args ) );
                ?>
            </div>
        </div>
    </li>
    <?php
    wp_nav_menu([
        'theme_location' => 'support',
        'items_wrap'     => '%3$s', // Remove the UL element.
        'container'      => false, // Remove the container div.
    ]);
    ?>
</ul>

这就是我使用 wordpress 设法解决这个问题的方法 walker_nav_menu https://developer.wordpress.org/reference/classes/walker_nav_menu/

class Custom_Walker_Nav_Menu extends Walker_Nav_Menu {

    /**
     * Unique id for dropdowns
     */
    public $submenu_unique_id = '';

    /**
     * Starts the list before the elements are added.
     * @see Walker::start_lvl()
     */
    public function start_lvl( &$output, $depth = 0, $args = array() ) {
        if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
            $t = '';
            $n = '';
        } else {
            $t = "\t";
            $n = "\n";
        }
        $indent = str_repeat( $t, $depth );

        $before_start_lvl = '<div class="megamenu">';

        if($depth==0){
            $output .= "{$n}{$indent}{$before_start_lvl}<ul id=\"$this->submenu_unique_id\" class=\"container megamenu-background sub-menu dropdown-content\">{$n}";
        }
        else{
            $output .= "{$n}{$indent}<ul id=\"$this->submenu_unique_id\" class=\"sub-menu dropdown-content\">{$n}";
        }

    }

    /**
     * Ends the list of after the elements are added.
     * @see Walker::end_lvl()
     */
    public function end_lvl( &$output, $depth = 0, $args = array() ) {
        if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
            $t = '';
            $n = '';
        } else {
            $t = "\t";
            $n = "\n";
        }
        $indent = str_repeat( $t, $depth );
        $after_end_lvl = '</div>';

        if($depth==0){
            $output .= "$indent</ul>{$after_end_lvl}{$n}";
        }
        else{
            $output .= "$indent</ul>{$n}";
        }

    }

    /**
     * @see Walker::start_el()
     */
    public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
        if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
            $t = '';
            $n = '';
        } else {
            $t = "\t";
            $n = "\n";
        }
        $indent = ( $depth ) ? str_repeat( $t, $depth ) : '';

        $classes = empty( $item->classes ) ? array() : (array) $item->classes;
        $classes[] = 'menu-item-' . $item->ID;

        // set active class for current nav menu item
        if( $item->current == 1 ) {
            $classes[] = 'active';
        }

        // set active class for current nav menu item parent
        if( in_array( 'current-menu-parent' ,  $classes ) ) {
            $classes[] = 'active';
        }

        /**
         * Filters the arguments for a single nav menu item.
         */
        $args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );

        // add a divider in dropdown menus
        if ( strcasecmp( $item->attr_title, 'divider' ) == 0 && $depth === 1 ) {
            $output .= $indent . '<li class="divider">';
        } else if ( strcasecmp( $item->title, 'divider') == 0 && $depth === 1 ) {
            $output .= $indent . '<li class="divider">';
        } else {
            $class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );

            //adding col-md-3 class to column
            if( in_array('menu-item-has-children', $classes ) ) {
                if( $depth === 1 ) {                    
                    $class_names = $class_names ? ' class="col-md-3 mega-menucolumn '.esc_attr( $class_names ) . '"' : '';
                } else {
                    $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
                }
            }else{
                $class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
            }

            $id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args, $depth );
            $id = $id ? ' id="' . esc_attr( $id ) . '"' : '';

            $output .= $indent . '<li' . $id . $class_names .'>';

            $atts = array();
            $atts['title']  = ! empty( $item->attr_title ) ? $item->attr_title : '';
            $atts['target'] = ! empty( $item->target )     ? $item->target     : '';
            $atts['rel']    = ! empty( $item->xfn )        ? $item->xfn        : '';

            if( in_array('menu-item-has-children', $classes ) ) {
                $atts['href']   = ' ';
                $this->submenu_unique_id = 'dropdown-'.$item->ID;
            } else {
                $atts['href']   = ! empty( $item->url ) ? $item->url  : '';
                $atts['class'] = '';
            }

            $atts['class'] .= 'menu-item-link-class ';

            $atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );

            $attributes = '';
            foreach ( $atts as $attr => $value ) {
                if ( ! empty( $value ) ) {
                    $value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
                    $attributes .= ' ' . $attr . '="' . $value . '"';
                }
            }

            if( ! in_array( 'icon-only' , $classes ) ) {

                $title = apply_filters( 'the_title', $item->title, $item->ID );

                $title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );
            }

            $item_output = $args->before;
            $item_output .= '<a'. $attributes .'>';

            // set icon on left side
            if( !empty( $classes ) ) {
                foreach ($classes as $class_name) {
                    if( strpos( $class_name , 'fa' ) !== FALSE ) {
                        $icon_name = explode( '-' , $class_name );
                        if( isset( $icon_name[1] ) && !empty( $icon_name[1] ) ) {
                            $item_output .= '<i class="fa fa-'.$icon_name[1].'" aria-hidden="true"></i> ';
                        }
                    }
                }
            }

            $item_output .= $args->link_before . $title . $args->link_after;

            if( in_array('menu-item-has-children', $classes) ){
                if( $depth == 0 ) {
                    $item_output .= ' <i class="fa fa-bolt" aria-hidden="true"></i>';
                }
            }

            $item_output .= '</a>';
            $item_output .= $args->after;

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

    /**
     * Ends the element output, if needed.
     *
     */
    public function end_el( &$output, $item, $depth = 0, $args = array() ) {
        if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
            $t = '';
            $n = '';
        } else {
            $t = "\t";
            $n = "\n";
        }
        $output .= "</li>{$n}";
    }

} //