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.
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.
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.
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
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!
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.
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!
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?
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.
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.
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.
You need to run composer install
in the theme directory when you deploy a Sage 9 theme:
That was in reply to Right Way to Add New Walker to Sage 9, right? Running composer install
in the theme doesn’t solve my problem.
Hard to help without anything to go off.
Please provide the exact code that you are using/changes that you have made to Sage.
Hi Ben! Thanks - will try.
I’m running Windows 10 with Trellis/Bedrock/Sage 9-beta.4, and Soil installed via composer and activated.
In sage/app/
I’ve added a file nav-foundation-walker.php
with QWp6t’s Foundation.php walker. So the file looks like this:
<?php
namespace App;
use Roots\Soil\Nav\NavWalker as SoilNavWalker;
/**
* Foundation 6 Navigation Walker
*
* @author QWp6t
* @license OSL-3.0
* @see https://gist.github.com/QWp6t/8f94b7096bb0d3a72fedba68f73033a5
*/
class FoundationWalker extends SoilNavWalker
{
public function __construct()
{
parent::__construct();
remove_filter('nav_menu_css_class', [$this, 'cssClasses'], 10);
add_filter('nav_menu_css_class', [$this, 'itemClasses'], 10, 4);
}
/**
* @param string $output
* @param int $depth
* @param array $args
* @SuppressWarnings(PHPMD.CamelCaseMethodName) This method overrides its parent
* @SuppressWarnings(PHPMD.UnusedFormalParameter) This method overrides its parent
*/
// @codingStandardsIgnoreLine
public function start_lvl(&$output, $depth = 0, $args = [])
{
$output .= '<ul class="submenu menu" data-submenu>';
}
/**
* @param $classes
* @param $item
* @param $args
* @param $depth
* @return array
* @SuppressWarnings(PHPMD.UnusedFormalParameter) This method overrides its parent
*/
public function itemClasses($classes, $item, /** @noinspection PhpUnusedParameterInspection */ $args, $depth)
{
return array_filter(array_map(function ($class) use ($depth) {
switch ($class) {
case 'menu-item-has-children':
return 'has-submenu';
default:
return $class;
}
}, parent::cssClasses($classes, $item)));
}
}
Then in sage/resources/functions.php
I added nav-foundation-walker
to the Sage required files:
/**
* 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', 'nav-foundation-walker']);
When I load the site in a browser I get the error:
Fatal error: Class 'Roots\Soil\Nav\NavWalker' not found in /srv/www/test.com/current/web/app/themes/sage/app/nav-foundation-walker.php
How do I use Composer and autoloading so that Roots\Soil\Nav\Navwalker
is available in nav-foundation.php
?
I’ve tried modifying Soil’s composer.json
file by adding:
"autoload": {
"psr-4": {
"Roots\\Soil\\Nav\\": "modules/"
}
},
And I’ve tried modifying Sage’s composer.json
file by adding:
"autoload": {
"psr-4": {
"App\\": "app/",
"Roots\\Soil\\Nav\\": "../../plugins/Soil/modules/"
}
},
Each time I ran composer update
to create a new vendor/autoload_psr4.php
file. I also tried something similar in Bedrock’s composer.json
file. None of these worked. What am missing here?