问题描述
我在 WP Admin 中定义了一个菜单,如下所示:
我想要在父母页面上显示侧栏上的所有子链接。例如,如果用户在我的 「关于我们」 页面上,我想要一个以绿色突出显示的 4 个链接的列表显示在侧栏上。
我查看了 wp_nav_menu()的文档,它似乎没有任何 built-in 方式来指定给定菜单的特定节点作为生成链接的起点。
我为 a similar situation 创建了一个解决方案,它依赖于页面父代创建的关系,但我正在寻找一个专门使用菜单系统的解决方案。任何帮助将不胜感激。
最佳解决方案
这仍然在我的脑海里,所以我重新审视了这个解决方案,而不是依赖于上下文:
add_filter( 'wp_nav_menu_objects', 'submenu_limit', 10, 2 );
function submenu_limit( $items, $args ) {
if ( empty( $args->submenu ) ) {
return $items;
}
$ids = wp_filter_object_list( $items, array( 'title' => $args->submenu ), 'and', 'ID' );
$parent_id = array_pop( $ids );
$children = submenu_get_children_ids( $parent_id, $items );
foreach ( $items as $key => $item ) {
if ( ! in_array( $item->ID, $children ) ) {
unset( $items[$key] );
}
}
return $items;
}
function submenu_get_children_ids( $id, $items ) {
$ids = wp_filter_object_list( $items, array( 'menu_item_parent' => $id ), 'and', 'ID' );
foreach ( $ids as $id ) {
$ids = array_merge( $ids, submenu_get_children_ids( $id, $items ) );
}
return $ids;
}
Usage
$args = array(
'theme_location' => 'slug-of-the-menu', // the one used on register_nav_menus
'submenu' => 'About Us', // could be used __() for translations
);
wp_nav_menu( $args );
次佳解决方案
嗨 @jessegavin:
导航菜单存储在自定义帖子类型和自定义分类法的组合中。每个菜单存储为自定义分类法 (即 wp_term_taxonomy
中的 nav_menu
) 中的术语 (即 「关于菜单」,位于 wp_terms
中) 。
每个导航菜单项目都存储为 post_type=='nav_menu_item'
(即 wp_posts
中的 「关于公司」(wp_posts
)),其属性以_menu_item_*
_menu_item_*
的 meta_key
前缀存储为后期元数据 (wp_postmeta
),其中_menu_item_menu_item_parent
是菜单项的父级 ID Nav 菜单项目帖子。
菜单和菜单项之间的关系存储在 wp_term_relationships
中,其中 object_id
与 Nav 菜单项的 $post->ID
相关,$term_relationships->term_taxonomy_id
与 wp_term_taxonomy
和 wp_terms
中统一定义的菜单有关。
我很确定可以钩住'wp_update_nav_menu'
和'wp_update_nav_menu_item'
在 wp_terms
中创建实际菜单,并在 wp_term_taxonomy
和 wp_term_relationships
中创建一个并行的关系,其中每个具有 sub-Nav 菜单项的 Nav 菜单项也成为自己的 Nav 菜单。
你也想钩住'wp_get_nav_menus'
(根据我几个月前所做的类似工作,我建议将其添加到 WP 3.0),以确保您的生成的导航菜单不会被管理员用户手动显示,否则他们会很快失去同步,那么你手上会有一个数据噩梦。
听起来像一个有趣和有用的项目,但是它比现在可以承受的更多的代码和测试,部分原因是因为同步数据的任何东西往往是一个 PITA 来解决所有的错误 (和因为付费客户正在迫使我完成工作。:) 但是配备上面的信息我很有动力的 WordPress 插件开发人员可以编写它,如果他们想。
当然,你现在意识到,如果你做代码,你有义务把它发回到这里,所以我们都可以从你的慷慨中受益! 🙂
第三种解决方案
@goldenapples:Your Walker Class 不起作用但这个想法真的很好。我根据你的想法创造了一个步行者:
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //invalid parameter
return $output;
if (empty($elements)) //nothing to walk
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// flat display
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* need to display in hierarchical order
* separate elements into two buckets: top level and children elements
* children_elements is two dimensional array, eg.
* children_elements[10][] contains all sub-elements whose parent is 10.
*/
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( 0 == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
/*
* when none of the elements is top level
* assume the first one must be root of the sub elements
*/
if ( empty($top_level_elements) ) {
$first = array_slice( $elements, 0, 1 );
$root = $first[0];
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( $root->$parent_field == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
}
$current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' ); //added by continent7
foreach ( $top_level_elements as $e ){ //changed by continent7
// descend only on current tree
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( !empty( $descend_test ) )
$this->display_element( $e, $children_elements, 2, 0, $args, $output );
}
/*
* if we are displaying all levels, and remaining children_elements is not empty,
* then we got orphans, which should be displayed regardless
*/
/* removed by continent7
if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
$empty_array = array();
foreach ( $children_elements as $orphans )
foreach( $orphans as $op )
$this->display_element( $op, $empty_array, 1, 0, $args, $output );
}
*/
return $output;
}
}
现在你可以使用:
<?php wp_nav_menu(
array(
'theme_location'=>'test',
'walker'=>new Selective_Walker() )
); ?>
输出是包含当前根元素的列表,它是孩子 (而不是他们的孩子) 。 Def:根元素:=与当前页面对应的顶级菜单项,或当前页面的父级或父级父级的顶级菜单项
这并不完全是回答原来的问题,而是差不多,因为还有顶级的项目。这对我来说很好,因为我想要顶级元素作为侧栏的标题。如果要摆脱这一点,您可能需要重写 display_element 或使用 HTML-Parser 。
第四种方案
这是一个步行者扩展,应该做你想要的:
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //invalid parameter
return $output;
if (empty($elements)) //nothing to walk
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// flat display
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* need to display in hierarchical order
* separate elements into two buckets: top level and children elements
* children_elements is two dimensional array, eg.
* children_elements[10][] contains all sub-elements whose parent is 10.
*/
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( 0 == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
/*
* when none of the elements is top level
* assume the first one must be root of the sub elements
*/
if ( empty($top_level_elements) ) {
$first = array_slice( $elements, 0, 1 );
$root = $first[0];
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( $root->$parent_field == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
}
$current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );
foreach ( $top_level_elements as $e ) {
// descend only on current tree
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( empty( $descend_test ) ) unset ( $children_elements );
$this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
}
/*
* if we are displaying all levels, and remaining children_elements is not empty,
* then we got orphans, which should be displayed regardless
*/
if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
$empty_array = array();
foreach ( $children_elements as $orphans )
foreach( $orphans as $op )
$this->display_element( $op, $empty_array, 1, 0, $args, $output );
}
return $output;
}
}
基于我以前在我的评论中引用的 mfields 代码。它所做的一切就是检查菜单中的步骤来查看当前的元素是否是 (1) 当前菜单项,或者 (2) 当前菜单项的祖先,并且只有当这些条件中的任何一个为真。希望这适合你。
要使用它,只需在调用菜单时添加一个”walker” 参数,即:
<?php wp_nav_menu(
array(
'theme_location'=>'test',
'walker'=>new Selective_Walker() )
); ?>
第五种方案
我为自己整理下列课程。它会找到当前页面的顶级导航父母,或者您可以在 walker 构造函数中给它一个目标顶部的导航 ID 。
class Walker_SubNav_Menu extends Walker_Nav_Menu {
var $target_id = false;
function __construct($target_id = false) {
$this->target_id = $target_id;
}
function walk($items, $depth) {
$args = array_slice(func_get_args(), 2);
$args = $args[0];
$parent_field = $this->db_fields['parent'];
$target_id = $this->target_id;
$filtered_items = array();
// if the parent is not set, set it based on the post
if (!$target_id) {
global $post;
foreach ($items as $item) {
if ($item->object_id == $post->ID) {
$target_id = $item->ID;
}
}
}
// if there isn't a parent, do a regular menu
if (!$target_id) return parent::walk($items, $depth, $args);
// get the top nav item
$target_id = $this->top_level_id($items, $target_id);
// only include items under the parent
foreach ($items as $item) {
if (!$item->$parent_field) continue;
$item_id = $this->top_level_id($items, $item->ID);
if ($item_id == $target_id) {
$filtered_items[] = $item;
}
}
return parent::walk($filtered_items, $depth, $args);
}
// gets the top level ID for an item ID
function top_level_id($items, $item_id) {
$parent_field = $this->db_fields['parent'];
$parents = array();
foreach ($items as $item) {
if ($item->$parent_field) {
$parents[$item->ID] = $item->$parent_field;
}
}
// find the top level item
while (array_key_exists($item_id, $parents)) {
$item_id = $parents[$item_id];
}
return $item_id;
}
}
导航电话:
wp_nav_menu(array(
'theme_location' => 'main_menu',
'walker' => new Walker_SubNav_Menu(22), // with ID
));
第六种方案
更新:我把它变成一个插件。 Download here 。
我需要自己解决这个问题,并最终卷入对菜单查找结果的过滤器。它允许您正常使用 wp_nav_menu
,但是基于父元素的标题来选择菜单的 sub-section 。将 submenu
参数添加到菜单中,如下所示:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => 'About Us',
));
你甚至可以通过把斜杠放在几个深度:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => 'About Us/Board of Directors'
));
或者如果您喜欢使用数组:
wp_nav_menu(array(
'menu' => 'header',
'submenu' => array('About Us', 'Board of Directors')
));
它使用标题的小插曲版本,这应该使它宽容诸如资本和标点符号之类的东西。
参考文献
注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。