用Timber添加多级菜单

我对Timber并不熟悉-实际上这是我第一次听说它, 但是我正在帮助一个慈善机构, 谁是Web开发人员让他们陷入困境。
我已经完成了大部分工作-除了使子菜单正常工作之外。
将项目添加到Wordpress中的子菜单位置时, 它们将显示在主菜单的同一级别上。
想知道是否有人可以帮助我?
下面是各种” 细枝” 的代码-但是让我知道你是否也需要函数代码来提供帮助。
menu.twig:

{% for item in menu %} < li class="{{ prefix }}__li {{ item.classes | join(' ') }}"> < a class="{{ prefix }}__a" target="{{ item.target }}" href="/uploads/allimg/220525/1T3444955-0.jpg item.link }}"> < span> {{ item.title }}< /span> < /a> {% include "menu.twig" with {'menu': item.get_children} %} < /li> {% endfor %} {% endif %}

头文件
< header class="header"> < div class="header__secondary"> < div class="container"> < div class="row"> < div class="col-xs-12"> < nav class="header__nav nav-secondary"> < ul class="nav-secondary__ul"> {% include "menu.twig" with { 'menu': menu.header_secondary.items, 'prefix': 'nav-secondary' } %} < li class="nav-secondary__li nav-secondary__li--cart"> < a class="nav-secondary__a cart-customlocation" href="/uploads/allimg/220525/1T3444955-0.jpg cart_url }}"> < /a> < /li> < /ul> < /nav> < /div> < /div> < /div> < /div> < div class="header__primary"> {% block header %} < div class="container"> < div class="row"> < div class="col-xs-6 col-md-2"> < a href="http://www.srcmini.com/" class="header__logo"> < img src="http://img.readke.com/220525/1T3444955-0.jpg site.theme.link }}/dist/img/logo.jpg" alt="{{ site.name }} Logo"> < /a> < /div> < div class="col-xs-6 col-md-10"> < nav class="header__nav nav" role="navigation"> < ul class="nav__ul"> {% include "menu.twig" with { 'menu': menu.header_primary.items, 'prefix': 'nav' } %} < /ul> < /nav> < div class="hamburger hamburger--spring"> < div class="hamburger-box"> < div class="hamburger-inner"> < /div> < /div> < /div> < /div> < /div> < /div> {% endblock %} < /div> < /header>

functions.php
< ?php /** * Timber starter-theme * https://github.com/timber/starter-theme * * @packageWordPress * @subpackageTimber * @sinceTimber 0.1 */if ( ! class_exists( 'Timber' ) ) { add_action( 'admin_notices', function() { echo '< div class="error"> < p> Timber not activated. Make sure you activate the plugin in < a href="' . esc_url( admin_url( 'plugins.php#timber' ) ) . '"> ' . esc_url( admin_url( 'plugins.php' ) ) . '< /a> < /p> < /div> '; }); add_filter('template_include', function( $template ) { return get_stylesheet_directory() . '/static/no-timber.html'; }); return; }Timber::$dirname = array('views'); Timber::$autoescape = false; /** * We're going to configure our theme inside of a subclass of Timber\Site * You can move this to its own file and include here via php's include("MySite.php") */ class StarterSite extends Timber\Site { /** Add timber support. */ public function __construct() { add_action( 'after_setup_theme', array( $this, 'theme_supports' ) ); add_filter( 'timber_context', array( $this, 'add_to_context' ) ); add_filter( 'get_twig', array( $this, 'add_to_twig' ) ); add_action( 'init', array( $this, 'register_post_types' ) ); add_action( 'init', array( $this, 'register_taxonomies' ) ); add_action( 'wp_enqueue_scripts', [$this, 'load_scripts'] ); parent::__construct(); register_nav_menu('header-secondary', 'Header Secondary'); register_nav_menu('header-primary', 'Header Primary'); remove_action('woocommerce_before_main_content', 'woocommerce_breadcrumb', 20, 0); // kill woo breadcrumbs remove_action( 'woocommerce_before_shop_loop' , 'woocommerce_catalog_ordering', 30 ); // kill woo sorting remove_action( 'woocommerce_before_shop_loop' , 'woocommerce_result_count', 20 ); // kill woo # results add_filter( 'woocommerce_add_to_cart_fragments', [$this, 'woocommerce_header_add_to_cart_fragment']); add_filter( 'woocommerce_get_image_size_gallery_thumbnail', function( $size ) { return array( 'width' => 400, 'height' => 400, 'crop' => 0, ); }); add_action( 'after_setup_theme', function() { add_theme_support( 'woocommerce' ); } ); }/** This is where you can register custom post types. */ public function register_post_types() {$name = "adoption"; $singular = "Adoption"; $plural = "Adoptions"; $labels = array( 'name' => _x("$plural", 'en'), 'singular_name' => _x("$singular", 'en'), 'all_items' => "All $plural", 'add_new' => _x("Add New $singular", 'en'), 'add_new_item' => _x("Add New $singular", 'en'), 'edit_item' => _x("Edit $singular", 'en'), 'new_item' => _x("New $singular", 'en'), 'view_item' => _x("View $singular", 'en'), 'search_items' => _x("Search $plural", 'en'), 'not_found' => _x("No $singular found", 'en'), 'not_found_in_trash' => _x("No $singular found in Trash", 'en'), 'parent_item_colon' => _x("Parent $singular:", 'en'), 'menu_name' => _x("$plural", 'en'), ); $args = array( 'labels' => $labels, 'hierarchical' => false, 'description' => $plural, 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions' ), 'taxonomies' => ['adoption_type'], 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 4, 'menu_icon' => 'dashicons-welcome-view-site', 'show_in_nav_menus' => true, 'publicly_queryable' => true, 'exclude_from_search' => false, 'has_archive' => true, 'query_var' => true, 'can_export' => true, 'rewrite' => true, 'capability_type' => 'post' ); register_post_type($name, $args); $name = "event"; $singular = "Event"; $plural = "Events"; $labels = array( 'name' => _x("$plural", 'en'), 'singular_name' => _x("$singular", 'en'), 'all_items' => "All $plural", 'add_new' => _x("Add New $singular", 'en'), 'add_new_item' => _x("Add New $singular", 'en'), 'edit_item' => _x("Edit $singular", 'en'), 'new_item' => _x("New $singular", 'en'), 'view_item' => _x("View $singular", 'en'), 'search_items' => _x("Search $plural", 'en'), 'not_found' => _x("No $singular found", 'en'), 'not_found_in_trash' => _x("No $singular found in Trash", 'en'), 'parent_item_colon' => _x("Parent $singular:", 'en'), 'menu_name' => _x("$plural", 'en'), ); $args = array( 'labels' => $labels, 'hierarchical' => false, 'description' => $plural, 'supports' => array( 'title', 'editor', 'thumbnail', 'revisions' ), 'public' => true, 'show_ui' => true, 'show_in_menu' => true, 'menu_position' => 4, 'menu_icon' => 'dashicons-welcome-view-site', 'show_in_nav_menus' => true, 'publicly_queryable' => true, 'exclude_from_search' => false, 'has_archive' => true, 'query_var' => true, 'can_export' => true, 'rewrite' => true, 'capability_type' => 'post' ); register_post_type($name, $args); if(function_exists('acf_add_options_page')) { acf_add_options_page([ 'page_title'=> 'Theme Content', 'menu_title'=> 'Theme Content', 'menu_slug'=> 'theme-content', 'capability'=> 'edit_posts', 'redirect'=> false ]); }if(function_exists('acf_add_options_page')) { acf_add_options_page([ 'page_title'=> 'Donation Settings', 'menu_title'=> 'Donation Settings', 'menu_slug'=> 'donation-settings', 'capability'=> 'edit_posts', 'redirect'=> false ]); }} /** This is where you can register custom taxonomies. */ public function register_taxonomies() {$labels = array( 'name'=> 'Adoption Types', 'singular_name'=> 'Adoption Type', 'menu_name'=> 'Adoption Type' ); $rewrite = array( 'slug'=> 'adoptions', 'with_front'=> true, 'hierarchical'=> false, ); $args = array( 'labels'=> $labels, 'hierarchical'=> true, 'public'=> true, 'show_ui'=> true, 'show_admin_column'=> true, 'show_in_nav_menus'=> true, 'show_tagcloud'=> false, 'rewrite'=> $rewrite, ); register_taxonomy( 'adoption_type', 'adoption', $args ); }public function load_scripts() { wp_enqueue_style( 'main', get_stylesheet_directory_uri() . '/dist/css/main.min.css' ); wp_enqueue_script('slick-js', get_stylesheet_directory_uri() . '/dist/js/plugins/slick.min.js', 'jquery', false, true); wp_enqueue_script('main-js', get_stylesheet_directory_uri() . '/dist/js/main.js', ['jquery', 'slick-js'], false, true); }/** This is where you add some context * * @param string $context context['this'] Being the Twig's {{ this }}. */ public function add_to_context( $context ) {$context['options'] = get_fields('options'); $context['menu']['header_primary'] = new Timber\Menu('header-primary'); $context['menu']['header_secondary'] = new Timber\Menu('header-secondary'); $context['shop_url'] = get_permalink(woocommerce_get_page_id('shop' )); $context['site'] = $this; if (get_the_ID()) { // load page modules require_once('src/modules.php'); }return $context; }public function theme_supports() { // Add default posts and comments RSS feed links to head. add_theme_support( 'automatic-feed-links' ); /* * Let WordPress manage the document title. * By adding theme support, we declare that this theme does not use a * hard-coded < title> tag in the document head, and expect WordPress to * provide it for us. */ add_theme_support( 'title-tag' ); /* * Enable support for Post Thumbnails on posts and pages. * * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ */ add_theme_support( 'post-thumbnails' ); /* * Switch default core markup for search form, comment form, and comments * to output valid HTML5. */ add_theme_support( 'html5', array( 'comment-form', 'comment-list', 'gallery', 'caption', ) ); /* * Enable support for Post Formats. * * See: https://codex.wordpress.org/Post_Formats */ add_theme_support( 'post-formats', array( 'aside', 'image', 'video', 'quote', 'link', 'gallery', 'audio', ) ); add_theme_support( 'menus' ); }/** This Would return 'foo bar!'. * * @param string $text being 'foo', then returned 'foo bar!'. */ public function myfoo( $text ) { $text .= ' bar!'; return $text; }/** This is where you can add your own functions to twig. * * @param string $twig get extension. */ public function add_to_twig( $twig ) { $twig-> addExtension( new Twig_Extension_StringLoader() ); $twig-> addFilter( new Twig_SimpleFilter( 'myfoo', array( $this, 'myfoo' ) ) ); return $twig; }function woocommerce_header_add_to_cart_fragment( $fragments ) { global $woocommerce; ob_start(); ?> < a class="cart-customlocation" href="http://www.srcmini.com/< ?php echo $woocommerce-> cart-> get_cart_url(); ?>" title="< ?php _e('View your shopping cart', 'woothemes'); ?> "> < ?php echo sprintf(_n('%d item', '%d items', $woocommerce-> cart-> cart_contents_count, 'woothemes'), $woocommerce-> cart-> cart_contents_count); ?> - < ?php echo $woocommerce-> cart-> get_cart_total(); ?> < /a> < ?php$fragments['a.cart-customlocation'] = ob_get_clean(); return $fragments; }}new StarterSite();

编辑:根据收到的反馈, menu.twig代码如下-但是, 现在不显示任何菜单:
{% if items|default(menu.items) %} < ul> {% for item in items|default(menu.items) %} < li class="{{ prefix }}__li {{ item.classes | join(' ') }}"> < a class="{{ prefix }}__a" target="{{ item.target }}" href="/uploads/allimg/220525/1T3444955-0.jpg item.link }}"> < span> {{ item.title }}< /span> < /a> {% include "menu.twig" with {'menu': item.get_children} %} < /li> {% endfor %} < /ul> {% endif %}

编辑2:下面是header.twig, 因为那里还有更多菜单项…
< header class="header"> < div class="header__secondary"> < div class="container"> < div class="row"> < div class="col-xs-12"> < nav class="header__nav nav-secondary"> < ul class="nav-secondary__ul"> {% include "menu.twig" with { 'menu': menu.menu_header_secondary.items, 'prefix': 'nav-secondary' } %} < li class="nav-secondary__li nav-secondary__li--cart"> < a class="nav-secondary__a cart-customlocation" href="/uploads/allimg/220525/1T3444955-0.jpg cart_url }}"> < /a> < /li> < /ul> < /nav> < /div> < /div> < /div> < /div> < div class="header__primary"> {% block header %} < div class="container"> < div class="row"> < div class="col-xs-6 col-md-2"> < a href="http://www.srcmini.com/" class="header__logo"> < img src="http://img.readke.com/220525/1T3444955-0.jpg site.theme.link }}/dist/img/logo.jpg" alt="{{ site.name }} Logo"> < /a> < /div> < div class="col-xs-6 col-md-10"> < nav class="header__nav nav" role="navigation"> < ul class="nav__ul"> {% include "menu.twig" with { 'menu': menu.menu_header_primary.items, 'prefix': 'nav' } %} < /ul> < /nav> < div class="hamburger hamburger--spring"> < div class="hamburger-box"> < div class="hamburger-inner"> < /div> < /div> < /div> < /div> < /div> < /div> {% endblock %} < /div> < /header>

编辑3-@Gchtr输入后的当前配置, 并获得正确的菜单以在正确的位置显示。仍然没有子菜单:
menu.twig:
{% set items = items|default(menu.items) %}{% if items %} < ul> {% for item in items %} < li class="{{ prefix }}__li {{ item.classes | join(' ') }}"> < a class="{{ prefix }}__a" target="{{ item.target }}" href="/uploads/allimg/220525/1T3444955-0.jpg item.link }}"> < span> {{ item.title }}< /span> < /a> {% if item.children %} {% include "menu.twig" with { items: item.children } %} {% endif %} < /li> {% endfor %} < /ul> {% endif %}

【用Timber添加多级菜单】header.twig:
< header class="header"> < div class="header__secondary"> < div class="container"> < div class="row"> < div class="col-xs-12"> < nav class="header__nav nav-secondary"> < ul class="nav-secondary__ul"> {% include "menu.twig" with { 'menu': menu_header_secondary, 'prefix': 'nav-secondary' } %} < li class="nav-secondary__li nav-secondary__li--cart"> < a class="nav-secondary__a cart-customlocation" href="/uploads/allimg/220525/1T3444955-0.jpg cart_url }}"> < /a> < /li> < /ul> < /nav> < /div> < /div> < /div> < /div> < div class="header__primary"> {% block header %} < div class="container"> < div class="row"> < div class="col-xs-6 col-md-2"> < a href="http://www.srcmini.com/" class="header__logo"> < img src="http://img.readke.com/220525/1T3444955-0.jpg site.theme.link }}/dist/img/logo.jpg" alt="{{ site.name }} Logo"> < /a> < /div> < div class="col-xs-6 col-md-10"> < nav class="header__nav nav" role="navigation"> < ul class="nav__ul"> {% include "menu.twig" with { 'menu': menu_header_primary, 'prefix': 'nav' } %} < /ul> < /nav> < div class="hamburger hamburger--spring"> < div class="hamburger-box"> < div class="hamburger-inner"> < /div> < /div> < /div> < /div> < /div> < /div> {% endblock %} < /div> < /header>

functions.php(Header Stuff的片段, 而不是全部片段):
public function add_to_context( $context ) {$context['options'] = get_fields('options'); $context['menu_header_primary']= new Timber\Menu( 'header-primary' ); $context['menu_header_secondary'] = new Timber\Menu( 'header-secondary') ; $context['shop_url'] = get_permalink(woocommerce_get_page_id('shop' )); $context['site'] = $this;

#1在add_to_context()方法的StarterSite类中, 你将像这样添加菜单:
$context['menu']['header_primary'] = new Timber\Menu('header-primary'); $context['menu']['header_secondary'] = new Timber\Menu('header-secondary');

现在, 当你想在Twig中使用access menu.items时, 此操作将无效, 因为你无法访问嵌套值。它应该是menu.header_primary.items。为了简化此操作, 我将其更改为使用单独的上下文条目:
$context['menu_header_primary']= new Timber\Menu( 'header-primary' ); $context['menu_header_secondary'] = new Timber\Menu( 'header-secondary') ;

然后, 对于Twig文件, 你始终需要传递菜单变量。
{% set items = items|default(menu.items) %}{% if items %} < ul> {% for item in items %} < li class="{{ prefix }}__li {{ item.classes | join(' ') }}"> < a class="{{ prefix }}__a" target="{{ item.target }}" href="/uploads/allimg/220525/1T3444955-0.jpg item.link }}"> < span> {{ item.title }}< /span> < /a> {% if item.children %} {% include "menu.twig" with { items: item.children } %} {% endif %} < /li> {% endfor %} < /ul> {% endif %}

编辑
在header.twig中, 你还需要从菜单包含中删除嵌套(menu_header_primary.items, 而不是menu.menu_header_primary.items。否则, Timber将采用它可以找到的第一个菜单。
< nav class="header__nav nav" role="navigation"> < ul class="nav__ul"> {% include "menu.twig" with { menu: menu_header_primary, prefix: 'nav' } %} < /ul> < /nav>

#2好像你缺少< ul> < / ul> 包装器, 或者你没有完整地发布代码:
{% if menu %} < ul> {% for item in menu %} < li class="{{ prefix }}__li {{ item.classes | join(' ') }}"> < a class="{{ prefix }}__a"target="{{ item.target }}" href="/uploads/allimg/220525/1T3444955-0.jpg item.link }}"> {{ item.title }}< /a> {% include "menu.twig" with {'items': item.children} %} < /li> {% endfor %} < /ul> {% endif %}

    推荐阅读