Пользовательский вывод меню wp nav (отображение всех дочерних элементов верхнего элемента меню в текущей ветви)


Я хотел бы создать функцию 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), и это можно сделать с помощью wp_nav_menus, поэтому нам не нужно его кодировать.

Проблемная часть состоит в том, чтобы показать в другом месте пункты подменю текущего основного элемента... Поэтому, если пользователь находится на странице "пункт меню 1", ничего не отображается. Если пользователь на странице "пункт меню 2" наше "новое" пользовательское меню показывает:

submenu item a
submenu item b

Одни и те же пункты меню отображаются, когда пользователь находится на одной из двух вышеперечисленных страниц (пункты меню).

Когда пользователь нажимает "пункт меню 3" и посещает его целевую страницу, он видит пункты подменю c, d, e, такие же, как и после нажатия на любой из этих пунктов подменю.

Если в меню есть элементы 3-го уровня (и глубже), пользовательское меню должно отображать все дочерние элементы текущего самого верхнего элемента меню, кроме самого верхнего элемента (который будет указан в верхняя часть сайта, описанного на первом месте)

Есть ли способ создать такую функциональность?

Идея близка к: Отобразить часть/ветвь меню просто он должен быть динамичным и отображать дочерние элементы текущей ветви меню/все дочерние элементы элемента ветви верхнего меню, если они существуют.

AFAIK Я не могу использовать get_ancestors, потому что он работает только с иерархическими таксономиями, и здесь мы не говорим о меню, созданном из иерархической структуры сообщений/страниц, но с использованием редактора меню.

РЕШЕНО: Кажется, мне удалось создать правильные функции, объединив несколько методов:

/**
 * 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 частей, потому что элемент меню верхнего уровня также может быть текущим.

Если найден элемент $ верхнего уровня, то функция submenu_get_children_ids используется для перечисления всех его дочерних элементов.

Author: Community, 2013-10-13

1 answers

Идея

Вместо того, чтобы создавать пользовательские ходунки, я подумал, что проще фильтровать элементы с помощью фильтра wp_nav_menu_objects крюк.

Этот крючок определен в /wp-includes/nav-menu-templates.php и когда он запускается, он переходит к функциям, подключающим к нему массив $sorted_menu_items, который содержит все элементы печатаемого меню, если функция изменит этот массив, добавив или удалив элементы, результирующее меню будет изменено.

Метод

Если применен фильтр к wp_nav_menu_objects напрямую, все меню будут отфильтрованы, поэтому я подумал, что лучше создать функцию, которая будет обертывать wp_nav_menu добавление фильтра перед вызовом wp_nav_menu и удаление его после : таким образом фильтруется только нужное меню.

Рабочий процесс фильтрации

  1. Цикл всех элементов, переданных wp_nav_menu_objects крючок фильтра
  2. Создайте 2 вспомогательных массива: один только из родительских идентификаторов, один с такими элементами, как $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 с активной темой twentytherteen и без других плагинов: в данном случае это сработало.

 5
Author: gmazzap, 2013-10-26 14:34:56