wprig
wprig copied to clipboard
Extending Walker Class is currently a Night-mare
Issue Overview
First off, I promised myself not to ask this very question anywhere, but honestly, this is beyond me (fed-up), I am trying to create a Mega site Navigation and after research upon researching, it seems the only way out is to extend the walker class, and re-engineer it to fit your desired output.
Describe your environment
Version: 1.37.1 (user setup) Date: 2019-08-15T16:17:55.855Z Electron: 4.2.7 Chrome: 69.0.3497.128 Node.js: 10.11.0 V8: 6.9.427.31-electron.0 OS: Windows_NT x64 10.0.10240
Steps to Reproduce
- Create a new Class under Nav_Menus -> Mega_Nav_Menu.php Since I will be creating a Mega Nav which opens and closes an element, I will be using the 'start_el' method and the 'end_el' method.
This is what I did:
<?php
/**
* WP_Rig\WP_Rig\Nav_Menus\Mega_Nav_Menu class
*
* @package wp_rig
*/
namespace WP_Rig\WP_Rig\Nav_Menus;
use function WP_Rig\WP_Rig\wp_rig;
use WP_Post;
use function add_filter;
use function add_action;
use function get_theme_mod;
/**
* Class for managing the mega navigation menu.
*/
class Mega_Nav_Menu {
const SLUG = 'Mega';
/**
* Adds the action and filter hooks to integrate with WordPress.
*/
public function initialize() {
add_filter( 'walker_nav_menu_start_el', [ $this, 'add_extra_elements_nav' ], 10, 4 );
add_filter( 'walker_nav_menu_end_el', [ $this, 'add_extra_elements_nav_end' ], 10, 4 );
}
/**
* Adds extra ul's to the parent ul e.g <ul><ul = "the extra ul"></ul></ul>
* This is the start_el of the walker class
*
* @param string $item_output The menu item's starting HTML output.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param object $args An object of wp_nav_menu() arguments.
* @return string Modified nav menu HTML.
*/
class my_walker extends Walker_Nav_Menu {
public function add_extra_elements_nav( string $item_output, WP_Post $item, int $depth, $args ) : string {
// Only for our primary menu location.
if ( empty( $args->theme_location ) || static::SLUG !== $args->theme_location ) {
return $item_output;
}
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
if ( in_array( 'new-column', $classes ) ) {
$output .= '</ul><ul class="sub-menu">';
}
$classes[] = 'menu-item-' . $item->ID;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
$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 : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
$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 . '"';
}
}
$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 );
// Check to see if this is a parent list item, if it is open the mega-menu div
if ( in_array( 'menu-item-has-children', $classes ) ) {
$output .= '<div class="mega-menu">';
}
}
/**
* Ends the waker class with necessary closings tags added to the start
*
* @param string $item_output The menu item's starting HTML output.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param object $args An object of wp_nav_menu() arguments.
* @return string Modified nav menu HTML.
*/
public function add_extra_elements_nav_end_el( string $item_output, WP_Post $item, int $depth, $args ) : string {
// Check to see if this is a parent list item, if it is close the mega-menu div
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
if ( in_array( 'menu-item-has-children', $classes ) ) {
$output .= '</div><!-- END mega-menu -->';
}
$output .= "</li>\n";
}
}
- Inside Nav_Menus -> Components.php I added this:
/**
* Adds the action and filter hooks to integrate with WordPress.
*/
public function initialize() {
$mega_nav_menu = new Mega_Nav_Menu();
$mega_nav_menu->initialize();
}
and this
public function template_tags() : array {
return [
'is_mega_nav_menu_active' => [ $this, 'is_mega_nav_menu_active' ],
'display_mega_nav_menu' => [ $this, 'display_mega_nav_menu' ],
];
}
Registered the Mega Nav Menu
/**
* Registers the navigation menus.
*/
public function action_register_nav_menus() {
register_nav_menus(
[
static::PRIMARY_NAV_MENU_SLUG => esc_html_x( 'Primary', 'nav menu', 'wp-rig' ),
Mega_Nav_Menu::SLUG => esc_html_x( 'Mega', 'nav menu', 'wp-rig' ),
]
);
}
checked whether Mega menu is active
public function is_mega_nav_menu_active() : bool {
return (bool) has_nav_menu( Mega_Nav_Menu::SLUG );
}
display mega menu
public function display_mega_nav_menu( array $args = [] ) {
if ( ! isset( $args['container'] ) ) {
$args['container'] = false;
}
$args['theme_location'] = Mega_Nav_Menu::SLUG;
wp_nav_menu( $args );
}
- Then, I went into the template I want the mega menu to display, the previous one was the primary menu, just replaced with the mega menu, the below is what I did
<?php
/**
* Template part for displaying the complete unique header
*
* @package wp_rig
*/
namespace WP_Rig\WP_Rig;
if ( ! wp_rig()->is_mega_nav_menu_active() ) {
return;
}
?>
<div class="site-logo">
<?php the_custom_logo(); ?>
</div><!-- .site-logo -->
<nav id="site-navigation" class="collapse main-navigation nav--toggle-sub nav--toggle-small" aria-label="<?php esc_attr_e( 'Main menu', 'wp-rig' ); ?>"
<?php
if ( wp_rig()->is_amp() ) {
?>
[class]=" siteNavigationMenu.expanded ? 'main-navigation nav--toggle-sub nav--toggle-small nav--toggled-on' : 'main-navigation nav--toggle-sub nav--toggle-small' "
<?php
}
?>
>
<?php
if ( wp_rig()->is_amp() ) {
?>
<amp-state id="siteNavigationMenu">
<script type="application/json">
{
"expanded": false
}
</script>
</amp-state>
<?php
}
?>
<button class="menu-toggle" aria-label="<?php esc_attr_e( 'Open menu', 'wp-rig' ); ?>" aria-controls="primary-menu" aria-expanded="false"
<?php
if ( wp_rig()->is_amp() ) {
?>
on="tap:AMP.setState( { siteNavigationMenu: { expanded: ! siteNavigationMenu.expanded } } )"
[aria-expanded]="siteNavigationMenu.expanded ? 'true' : 'false'"
<?php
}
?>
>
<?php esc_html_e( 'Menu', 'wp-rig' ); ?>
</button>
<div class="mega-menu-container">
<?php wp_rig()->display_mega_nav_menu( [ 'menu_id' => 'primary-menu' ] ); ?>
</nav><!-- #site-navigation -->
Expected Behavior
I expected the extended walker class or menu to be created
Current Behavior
I had an unexpected behaviour, see image https://prnt.sc/ovldzr
Possible Solution
Normally, to create a walker class of the kind I want,
There should be a class like this:
class my_walker extends Walker_Nav_Menu {
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) { // Code for start_el goes here }
public function end_el( &$output, $item, $depth = 0, $args = array() ) { // Code for end_el goes here }}
--
Give us an option to add a new class to the nav_menu
Screenshots / Video
Related Issues and/or PRs
This is a @felixarntz question
Related to #560
Wprig is currently the best starter theme I have ever tried, so I resorted to using a plugin to handle the Mega Navigation.
@Horlaes , just wondering a few things about your code sample.
-
noticed youre started with
class Mega_Nav_Menu
and are usinginitialize()
, but didnt includeimplements Component_Interface
. Which if I understand correctly (I might not) means the theme doesnt know what to do withinitialize()
. -
Similarly, did you try
class Mega_Nav_Menu extends \Walker_Nav_Menu implements Component_Interface
instead of resorting to a subclass? As I wrote in the related issue that was posted above, that declaration worked fine for me with\WP_Widget
.
1.) I don't need to implement the component interface anymore, as that has been used in the main Nav component and If you carefully read what I posted above, you'll notice I created the Mega Nav file under a parent folder (Nav_Menu in this case, and it has the component loaded already), then inside the main Component (Component.php), I added the call to it.
2.) Widget is a different case, I need a way to walk through the menu items, I already achieved this with my previous themes, I think it is not just supported yet or perhaps it is, which I don't know how to achieve, I have ruled that off my list currently (spent close to 2 weeks before asking).
Thanks for the heads up man, appreciate!
Hello @Horlaes you manage to solve this detail, could you please share code to see how you solved?. Thanks for your help
or someone please managed to do this from the menu.
@mor10 Hello please can you tell me how to make or add the wallker to the menu please?.
@dackr the walker class is currently not supported in wp-rig, you can't extend the walker-class, the only thing you can do is to create a plugin or use download one from the repo. This stack overflow answer should also help: https://wordpress.stackexchange.com/questions/116708/customizing-walker-nav-menu
@Horlaes Thanks for your help.
I want this topic to be important for a wprig improvement
Any update about this?? It's not the walker clases supported yet? What's the status of this improvement?? Thank you for the good work done in wprig @mor10 @Horlaes
If I try and require a file, say my-custom-walker-class.php, and then call the walker from header.php, I get an error similar to:
: Uncaught Error: Class 'WP_Rig\WP_Rig\Custom_Walker_Nav_Main_Menu' not found in /www/kinsta/public/devmysite/wp-content/themes/wprig/header.php:112 Stack trace: #0 /www/kinsta/public/devmysite/wp-includes/template.php(730): require_once() #1 /www/kinsta/public/devmysite/wp-includes/template.php(676): load_template() #2 /www/kinsta/public/devmysite/wp-includes/general-template.php(48): locate_template() #3 /www/kinsta/public/devmysite/wp-content/themes/wprig/home-page-template.php(17): get_header() #4 /www/kinsta/public/devmysite/wp-includes/template-loader.php(106): include('/www/kinsta/pub...') #5 /www/kinsta/public/devmysite/wp-blog-header.php(19): require_once('/www/kinsta/pub...') #6 /www/kinsta/public/devmysite/index.php(17): require('/www/kinsta/pub...') #7 {main} thrown in
What exactly is going on here? Why can't I require a class directly? Where is load_template coming into play?
@ChrisBuck-Harness how are you including Custom_Walker_Nav_Main_Menu
?
WP_Rig doesn't have real autoloading (it only autoloads the various new Component()
statements....