Right Way to Add New Walker to Sage 9

Hi guys! I’m trying out Sage 9 beta 2 with Foundation for Sites.

I was wondering what the right way to add a new walker was, since the file structure & workflow are brand new.

I’m trying to implement the Drilldown menu seen here: https://wlcdesigns.com/2015/11/foundation-6-menu-walker-class-for-wordpress/

Do I add a new file (ie. navigation.php) to the src folder, which includes the App namespace? (Saw this on another topic)

I’m pretty lost so if anyone could point me in the right direction I’d greatly appreciate it.

PS: I have Soil installed, should I disable its walker?

3 Likes

This would be a fine way if you don’t want to put it somewhere else (like setup or something). Just remember to add that file to this array in functions.php. App namespace will be fine.

No need, as you’re overriding the walker when you do this:

'walker' => new F6_DRILL_MENU_WALKER(),

Let us know how it goes.

2 Likes

Lately I’ve been adding files to src like it’s my job. I usually add a file called menus.php for walkers and other filters, and I’ll often have separate files named after plugins (facetwp.php, woocommerce.php) so that I can find my custom functions and filters easily when I inevitably do something wrong.

2 Likes

Yeah, I try to keep out of the ‘core’ Sage files as much as possible. Hadn’t thought about the plugin idea, pretty sweet as well!

Even better would be to use psr-4 (or psr-0) and add the namespace and path to composer.json.

The only reason we manually load files like that in functions.php is because everything in sage/app (formerly sage/src) is procedural code that can’t make use of spl autoloader. Everything in lib is classes, which are autoloaded by composer. So just add your own namespace (I use use App\Theme or something similar) and whip up dem classes ezpz

I have a Foundation Sites NavWalker that extends the one used in Soil. I can share that later when I get home if anyone is interested.

3 Likes

Great, this worked like a charm. Thank you very much @JulienMelissas. The App namespace booted an error, but I removed it and everything works great.

On a side note, for anyone interested:

I got this working without any walker at all before reading @JulienMelissas’s answer, the following way (but his way is much better, use that one):

@php
    mainNavArgs = [
        'container' => 'ul',
        'menu' => __('Primary Navigation', 'sage'),
        'menu_class' => 'vertical menu',
        'theme_location' => 'primary_navigation',
        'items_wrap' => '<ul id="%1$s" class="%2$s" data-drilldown="" data-close-on-click>%3$s</ul>'
   ]
@endphp

{!! wp_nav_menu($mainNavArgs) !!}

The menu will still be rendered as a Drilldown, and in fact will add less classes to the elements than the other F6 Walker.

There are some caveats and you will probably need a little jQuery to add the vertical menu classes to the submenus (elements with children):

$('.is-drilldown-submenu').addClass('vertical menu'); on common.js should do the trick.

You’ll also need some CSS to make up for the fact that elements won’t have the exact same classes. I just added:

.is-drilldown-submenu {
  margin: 0;

  &.is-active {
    z-index: 0;
  }
}

…and everything ran smoothly.

Again, you’re probably better off following @JulienMelissas’s instructions and using the Walker which obviously gives you more control over the content, but if you’re looking for a quick, no-frills, beginner-friendly way to do it, this should help.

That would be great! I just created a repo on Github using the Walker I’d found but yours is probably better since it implements Soil’s walker. Please feel free to fork/PR/whatever.

I’m interested in seeing your implementation if you don’t mind.

I don’t know how my NavWalkers stack up against what’s already out there. I wrote these for my specific needs in two different projects, and they worked for me. Neither were intended for wider distribution, but here you go anyway. One is for Bootstrap and the other is for Foundation.

4 Likes

Maybe it will help someone.
I have situation like this in sage9-beta3.

I created new class file in new folder called Classes inside sage/app/lib/Sage/. I’ve created MainWalker.php file that looks like this:

MainWalker.php

namespace Roots\Sage\Classes;

use Roots\Soil\Nav\NavWalker as NavWalker;

/*
 * @author brunekninja
 */
class MainWalker extends NavWalker 
{
}

After that I just use that class as walker in my new nav menu inside header.blade.php

header.blade.php

<header class="page-header" data-method="stickyHeader">
  <div class="container">
    <nav class="site-navigation">

      @if (has_nav_menu('primary_navigation'))
        {!! wp_nav_menu([
        'theme_location' => 'primary_navigation',
        'menu_class' => 'site-navigation__main-nav',
        'container_class' => 'site-navigation__main-nav',
        'walker' => new Roots\Sage\Classes\MainWalker()
        ]) !!}
      @endif

That’s it, now you can create your custom nav walker in sage 9.
Hope it helps someone.

1 Like

Hi bruno. I tried this approach as it seemed very clean but I’m getting Fatal error: Class ‘Roots\Soil\Nav\NavWalker’ not found.
Used your code verbatim but am on Sage 9 beta 4 so i created the MainWalker class under /app and included it in functions.php as a required Sage include. Seen a lot of talk of namespace issues :confused:

1 Like

That method requires having Soil installed and activated

yep. i have installed and activated. Can see the clean soil nav structure.

Update, i resolved this with the help of smutek’s gist… FTW!

1 Like

Hey,

yes beta 4 has different structure, and also it is true that this approach requires Soil to be activated. But eventually you can just extend main navwalker class from WP and it has to work.

I checked the new structure, and there is small difference. I just putted navigation folder inside app.

48

Only thing that has to be changed is namespace, and call of nav walker.

So you have situation like this.

namespace App\Navigation;

this is namespace inside MainWalker file.

@if (has_nav_menu('primary_navigation'))
    {!! wp_nav_menu([
     'theme_location' => 'primary_navigation',
     'menu_class' => 'site-navigation__main-nav',
     'walker' => new \App\Navigation\MainWalker()
    ]) !!}     
@endif

And it works!

3 Likes

I followed the instructions of @brunekninja, but have the same problem as @thewhipster: Fatal error: Class ‘Roots\Soil\Nav\NavWalker’ not found.

I’m using Trellis/Bedrock/Sage 9 beta 4, with Soil installed and activated.

The same question was asked here, but never resolved.

Any other thoughts on why the class can’t be found, even though Soil is installed and activated?

1 Like

Hi, I had that problem on linux server in production, before that everything worked fine in docker etc.
Eventually I’ve added this in composer.json file.

"autoload": {
    "psr-4": {
     "App\\": "web/app/themes/sage/app/",
     "App\\Widgets\\": "web/app/themes/sage/app/widgets/"
    }
 }

After I registered classes this way, error have disappeared

You can try this, this is the only solution that I can think of.

1 Like

I tried brunekninja’s suggestion and several other autoload options in composer.json files for Bedrock, Sage, and Soil without success. I finally got this to work by loading the Foundation walker file on its own after Soil modules are loaded (and not including the Foundation walker in the Sage required files array). At the bottom of soil.php the modules are loaded with after_setup_theme, priority 100. So my Sage functions.php file now has this:

/**
 * Sage required files
 *
 * The mapped array determines the code library included in your theme.
 * Add or remove files to the array as needed. Supports child theme overrides.
 */
array_map(function ($file) use ($sage_error) {
    $file = "../app/{$file}.php";
    if (!locate_template($file, true, true)) {
        $sage_error(sprintf(__('Error locating <code>%s</code> for inclusion.', 'sage'), $file), 'File not found');
    }
}, ['helpers', 'setup', 'filters', 'admin']);

/**
 * Foundation Nav Walker
 *
 * Load the Foundation nav walker after Soil plugin modules are loaded so that Soil's NavWalker class is available.
 */
add_action( 'after_setup_theme', 'add_foundation_nav_walker', 101 );
function add_foundation_nav_walker() {

    if ( class_exists('\Roots\Soil\Nav\NavWalker') ) {

        require get_template_directory() . '/../app/nav-foundation-walker.php';
    }
}

I’m new to psr-4 and namespaces in php, but I gather that the reason for autoloading with psr-4 is to allow classes to be available independently of how they might otherwise be loaded via require, etc. Soil’s compser.json file currently doesn’t autoload anything. Should it include the following?

"autoload": {
    "psr-4": { "Roots\\Soil\\Nav": "modules" }
 },

When I try that and run composer update in Soil, the generated autoload file at soil/vendor/composer/autoload_psr4.php file correctly shows:

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Roots\\Soil\\Nav\\' => array($baseDir . '/modules'),
    'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'),
);

But if I then load nav_foundation_walker.php via the Sage required array I still get “Fatal error: Class ‘Roots\Soil\Nav\NavWalker’ not found” when loading the site in my browser. I assume the correct way to do this is with autoloading, but I’m not sure how to make that work.

1 Like

I’m having a somewhat similar problem. I have my Walkers working on development with no problems, but when I upload the theme to the prod server, I get the same “Walker not found error”. Weird.

1 Like

You need to run composer install in the theme directory when you deploy a Sage 9 theme: