wp_nav_menu()を使用してメニューツリーの部分/ブランチを表示
質問
WP Adminでこのように見えるメニューが定義されています。
親ページにいるときはいつでも、サイドバーにすべての子供リンクを表示できるようになりたいです。たとえば、ユーザーが「私たちについて」ページにいる場合、Greenで強調表示されている4つのリンクのリストがサイドバーに表示されます。
ドキュメントを見ました wp_nav_menu() また、リンクを生成する際に開始点として使用する特定のメニューの特定のノードを指定する組み込みの方法がないようです。
のソリューションを作成しました 同様の状況 ページの親によって作成された関係に依存していましたが、メニューシステムを具体的に使用するものを探しています。どんな助けも感謝します。
解決
これはまだ私の心にあったので、私はそれを再訪してこの解決策をまとめました。それは文脈にそれほど依存していません。
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;
}
使用法
$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 );
他のヒント
@goldenapples: あなたのウォーカークラス 動作しません。しかし、アイデアは本当に良いです。私はあなたのアイデアに基づいて歩行者を作成しました:
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を使用する必要がある場合があります。
やあ @jessegavin:
NAVメニューは、カスタムポストタイプとカスタム分類法の組み合わせに保存されます。各メニューは用語として保存されます(つまり、 「メニューについて」, 、で見つかりました wp_terms
)カスタム分類法(すなわち nav_menu
, 、で見つかりました wp_term_taxonomy
.)
各NAVメニュー項目は、の投稿として保存されます post_type=='nav_menu_item'
(すなわち 「会社について」, 、で見つかりました wp_posts
)ポストメタとして保存された属性を使用して(で wp_postmeta
) を使って meta_key
のプレフィックス _menu_item_*
どこ _menu_item_menu_item_parent
メニュー項目の親ナビゲーションメニュー項目投稿のIDです。
メニューとメニュー項目の関係はに保存されます wp_term_relationships
どこ object_id
に関連しています $post->ID
NAVメニュー項目と $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
サブNAVメニュー項目を持つすべてのNAVメニュー項目も、独自のNAVメニューになります。
あなたもしたいでしょう 針 'wp_get_nav_menus'
(私は数ヶ月前に行っていたいくつかの同様の作業に基づいて、WP 3.0に追加することを提案しました) 生成されたNAVメニューが管理者のユーザーによる操作のために表示されないようにするために、それ以外の場合は、非常に速く同期しなくなり、データの悪夢が手に入ります。
楽しくて便利なプロジェクトのように聞こえますが、データを同期するものはすべて、すべてのバグをアイロンをかけるとピタになる傾向があるため、今すぐ取り組む余裕があるよりも少しコードとテストがあります。 (そして、クライアントに支払うことが私に物事を成し遂げるように押し付けているからです。:) しかし、上記の情報を武装して、私はやる気のあるWordPressプラグイン開発者が望むならコーディングできます。
もちろん、コードをすると、ここに投稿する義務があるので、私たち全員があなたの大量に恩恵を受けることができるようになりました! :-)
これはあなたが探していることをするべきである歩行者の拡張機能です:
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)現在のメニュー項目の祖先であるかを確認し、それらの条件のいずれかが真である場合にのみ、その下のサブツリーを展開します。 。これがあなたのために働くことを願っています。
それを使用するには、メニューを呼び出すときに「歩行者」引数を追加するだけです。
<?php wp_nav_menu(
array(
'theme_location'=>'test',
'walker'=>new Selective_Walker() )
); ?>
アップデート: これをプラグインにしました。 ここからダウンロード.
私はこれを自分で解決する必要があり、最終的にはメニュールックアップの結果についてフィルターを書くことになりました。使用できます wp_nav_menu
通常どおりですが、親要素のタイトルに基づいてメニューのサブセクションを選択します。 Aを追加します 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')
));
タイトルのスラッグバージョンを使用しているため、首都や句読点のようなものを許すはずです。
私は自分のために次のクラスをまとめました。現在のページの上部NAV親が見つかります。または、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;
}
}
NAVコール:
wp_nav_menu(array(
'theme_location' => 'main_menu',
'walker' => new Walker_SubNav_Menu(22), // with ID
));
@davidn @hakreこんにちは、html-parserまたはoverriding display_elementのない醜い解決策があります。
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 );
}
*/
/*added by alpguneysel */
$pos = strpos($output, '<a');
$pos2 = strpos($output, 'a>');
$topper= substr($output, 0, $pos).substr($output, $pos2+2);
$pos3 = strpos($topper, '>');
$lasst=substr($topper, $pos3+1);
$submenu= substr($lasst, 0, -6);
return $submenu;
}
}
NAVメニューの出力には、現在のアイテム、現在のアイテムの祖先などの多くのクラスが含まれています。状況によっては、NAVツリーの出力全体を使用してCSSを使用してそれを塗りつぶして、やりたいことをすることができました。現在のページなどの子供のみ。
役立つはずの修正されたウォーカーを作りました!完璧ではありません - それはいくつかの空の要素を残しますが、それはトリックをします。変更は基本的にそれらの$ current_branchビットです。それが誰かを助けることを願っています!
class Kanec_Walker_Nav_Menu extends Walker {
/**
* @see Walker::$tree_type
* @since 3.0.0
* @var string
*/
var $tree_type = array( 'post_type', 'taxonomy', 'custom' );
/**
* @see Walker::$db_fields
* @since 3.0.0
* @todo Decouple this.
* @var array
*/
var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );
/**
* @see Walker::start_lvl()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param int $depth Depth of page. Used for padding.
*/
function start_lvl(&$output, $depth) {
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<ul class=\"sub-menu\">\n";
}
/**
* @see Walker::end_lvl()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param int $depth Depth of page. Used for padding.
*/
function end_lvl(&$output, $depth) {
global $current_branch;
if ($depth == 0) $current_branch = false;
$indent = str_repeat("\t", $depth);
$output .= "$indent</ul>\n";
}
/**
* @see Walker::start_el()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param int $current_page Menu item ID.
* @param object $args
*/
function start_el(&$output, $item, $depth, $args) {
global $wp_query;
global $current_branch;
// Is this menu item in the current branch?
if(in_array('current-menu-ancestor',$item->classes) ||
in_array('current-menu-parent',$item->classes) ||
in_array('current-menu-item',$item->classes)) {
$current_branch = true;
}
if($current_branch && $depth > 0) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $value . $class_names .'>';
$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
/**
* @see Walker::end_el()
* @since 3.0.0
*
* @param string $output Passed by reference. Used to append additional content.
* @param object $item Page data object. Not used.
* @param int $depth Depth of page. Not Used.
*/
function end_el(&$output, $item, $depth) {
global $current_branch;
if($current_branch && $depth > 0) $output .= "</li>\n";
if($depth == 0) $current_branch = 0;
}
}
私のプラグインのコードをチェックするか、あなたの目的のためにそれを使用してください;)
このプラグインは、拡張された「ナビゲーションメニュー」ウィジェットを追加します。ウィジェットを介してカスタムメニューの出力をカスタマイズするように設定できる多くのオプションを提供します。
機能は次のとおりです。
- カスタム階層 - 「関連するサブアイテムのみ」または「厳密に関連するサブアイテムのみ」。
- ディスプレイ +フラットディスプレイへの開始深度と最大レベル。
- 選択したものから始まるすべてのメニューアイテムを表示します。
- 現在の要素への直接的なパスのみまたはの子供のみを表示する
選択されたアイテム(親アイテムを含めるオプション)。 - ウィジェットブロックのカスタムクラス。
- WP_NAV_MENU関数のほぼすべてのパラメーター。