web-dev-qa-db-ja.com

カスタムwp_nav_menu出力(現在のブランチのトップメニュー要素のすべての子要素を表示)

私はwalker関数を作成するか、それともカスタム出力を生成できるように現在のwp_nav_menusを修正したいと思います。この構造を想像してみましょう:

menu item 1
menu item 2
 submenu item a
 submenu item b
menu item 3
 submenu item c
 submenu item d
 submenu item e

最も簡単なのは、メインメニュー項目(メインメニュー1,2,3)だけを1か所に表示することです。これはwp_nav_menusで実行できるので、コーディングする必要はありません。

問題のある部分は、現在メイン項目のサブメニュー項目を他の場所に表示することです...したがって、ユーザーが「メニュー項目1」ページにいる場合、何も表示されません。ユーザーが「メニュー項目2」ページにいる場合は、「新しい」カスタムメニューが表示されます。

submenu item a
submenu item b

ユーザーが上記の2つのページ(メニュー項目)のどちらかにいる場合、同じメニュー項目が表示されます。

ユーザーが「メニュー項目3」をクリックしてそのターゲットページにアクセスすると、それらのサブメニュー項目のいずれかをクリックした後と同じように、サブメニュー項目c、d、eが表示されます。

メニューに3番目のレベルの要素がある場合(およびそれより深い場合)、カスタムメニューには、現在の最上位のmenu要素のすべての子要素が、その最上位要素自体の横に表示されます。

そのような機能を作成する方法はありますか?

メニューの一部/ブランチを表示する 現在のメニューブランチの子エレメント/トップメニューブランチエレメントのすべての子エレメントが存在する場合はそれを表示することだけが必要です。

私はget_ancestorsを使用することはできません。なぜなら、それは階層的分類法でのみ機能するからです。ここではpost/pages階層構造から作成されたメニューについてではなく、メニューエディタを使用します。

SOLVED:いくつかの方法を組み合わせて正しい関数を作成できたようです。

/**
 * The API function, just a wrap for wp_nav_menu adding filter first and removing after
 */ 
function filtered_nav_menu( $args = array() ) {
    $echo = isset($args['echo']) ? (bool)$args['echo'] : true;
    $args['echo'] = false;
    add_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
    $menu = wp_nav_menu( $args );
    remove_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
    if ( $echo ) echo $menu;
    else return $menu;
}

/**
 * The filter callback, return the filtered elements
 */
function gmfnv_filter( $items ) {
    $found_top_parent_ID = false;
    foreach ($items as $item) {
        if ( ($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1) ) {
            $found_top_parent_ID = $item->ID;
        }
    }
    $children  = submenu_get_children_ids( $found_top_parent_ID, $items );
    foreach ( $items as $key => $item ) {
        if ( ! in_array( $item->ID, $children ) )
            unset($items[$key]);
    }
    return $items;
}

/**
 * Helper function: return children of an element using wp_filter_object_list
 */
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;
}

それは何をしますか - それは各$ items要素を通過してチェックすることで現在のブランチのトップの祖先を検索します。

($item->menu_item_parent == 0 && $item->current_item_ancestor == 1) || ($item->menu_item_parent == 0 && $item->current == 1)

最上位レベルのメニュー要素も現在のものである可能性があるため、この条件は2部構成にする必要があります。

トップレベルの$ itemが見つかると、関数submenu_get_children_idsがそのすべての子要素をリストするのに使われます。

3
Marcin Bobowski

アイデア

カスタムウォーカーを作成する代わりに、フィルターwp_nav_menu_objectsフックを使用してアイテムをフィルター処理する方が簡単だと思いました。

このフックは /wp-includes/nav-menu-templates.php で定義され、起動されると、関数がその配列を変更し、アイテムを追加または削除する場合、印刷されるメニューのすべての要素を含む配列$sorted_menu_itemsをフックする関数に渡します、結果のメニューが変更されます。

方法

wp_nav_menu_objectsへのフィルターが直接適用される場合、allメニューがフィルターされるので、関数を作成する方がよいと考えましたこれはwp_nav_menuをラップしてフィルターを追加しますbeforewp_nav_menuを呼び出して削除しますafter:このようにして、必要なメニューのみがフィルターされます。

フィルターワークフロー

  1. wp_nav_menu_objects filterフックによって渡されたすべてのアイテムを循環させます
  2. 2つのヘルパー配列を作成します。1つは親IDのみ、もう1つは$itemid => $parentidのようなアイテムを持つ
  3. アイテムをループしながら、アイテムのURLが現在のURLと一致することも確認します
  4. uRLが一致しない場合、親アイテムのみを返します
  5. ヘルパー配列を使用してURLが一致する場合、必要な要素を返します

コード

私がそのために書いたコードは5つの関数を使用するので、ここにコードをすべて含むプラグインを作成します。

<?php
/**
 * Plugin Name: Filtered Nav Menus
 * Author: Giuseppe  Mazzapica
 * Plugin URI: http://wordpress.stackexchange.com/questions/118720/
 * Author URI: http://wordpress.stackexchange.com/users/35541/
 */

/**
 * The API function, just a wrap for wp_nav_menu adding filter first and removing after
 */ 
function filtered_nav_menu( $args = array() ) {
  $echo = isset($args['echo']) ? (bool)$args['echo'] : true;
  $args['echo'] = false;
  add_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
  $menu = wp_nav_menu( $args );
  remove_filter( 'wp_nav_menu_objects', 'gmfnv_filter', 999 );
  if ( $echo ) echo $menu;
  else return $menu;
}

/**
 * The filter callback, return the filtered elements
 */
function gmfnv_filter( $items ) {
  $found = false;
  $parents = $items_tree = $allowed = array();
  $all_items = $items;
  while ( ! empty( $items ) ) {
    $item = array_shift( $items );
    $items_tree[$item->ID] = $item->menu_item_parent;
    if ( (int) $item->menu_item_parent == 0 ) $parents[] = $item->ID;
    if ( isset($item->current) && $item->current ) $found = $item->ID;
  }
  if ( ! $found ) {
    $ids = $parents;
  } else {
    $tree = gmfnv_get_tree( $found, $all_items, $items_tree );
    $ids = array_merge( $parents, $tree );
  }
  foreach ( $all_items as $item ) {
    if ( in_array( $item->ID, $ids ) ) $allowed[] = $item;
  }
  return $allowed;
}


/**
 * Helper function: take the matched element if and the helper array and
 * return the item ancestors by gmfnv_get_parents,
 * and the children of these ancestors returned by gmfnv_get_parents
 * using gmfnv_get_parents
 */
function gmfnv_get_tree( $test, $items, $tree ) {
  $parents = gmfnv_get_parents( $test, $items );
  $parents[] = $test;
  $n = array();
  foreach ( $parents as $parent ) {
    $n = array_merge( $n, gmfnv_get_childrens( $parent, $tree ) );
  }
  return array_unique( $n );
}


/**
 * Helper function: return ancestors of an element using the helper array
 */
function gmfnv_get_parents( $test, $items ) {
  $parents = array();
  foreach( $items as $item ) {
      if (
        (isset($item->current_item_ancestor) && $item->current_item_ancestor)
        || (isset($item->current_item_ancestor) && $item->current_item_ancestor)
      ) $parents[] = $item->ID;
  }
  return $parents;
}


/**
 * Helper function: return children of an element using the helper array
 */
function gmfnv_get_childrens( $test, $tree ) {
  $children = array();
  foreach ( $tree as $child => $parent ) {
    if ( $parent == $test ) $children[] = $child;
  }
  return $children;
}

の仕方

このプラグインを含むファイルを作成し、pluginsフォルダーに入れてアクティブにします。

必要に応じてメニューをフィルタリングする場合は、代わりにwp_nav_menu( $args )を使用します

filtered_nav_menu( $args );

免責事項

提供されているコード現状のまま、保証なし、ただしすぐにPHP 5.4、WP 3.7でテストされ、20のテーマがアクティブで、他のプラグインはありません。この場合。

5
gmazzap