Динамически исключать пункты меню из wp_nav_menu

17

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

1. Фон:

Я собрал меню Dock, используя пользовательские меню WP (wp_nav_menu) и jqDock на моем сайте. Поскольку jqDock нужны непрерывные изображения или ссылки на изображения, чтобы творить свою магию, я использую пользовательский обходчик, поэтому вывод HTML меню навигации выглядит примерно так:

<div id="menu-first" class="nav">
<a><img src="http://path/to/image-1.png"/></a>
<a><img src="http://path/to/image-2.png"/></a>
<a><img src="http://path/to/image-3.png"/></a>
etc...
</div>

Код для моего пользовательского ходунка:

class custom_nav_walker extends Walker_Nav_Menu 
{
    var $tree_type = array( 'post_type', 'taxonomy', 'custom' );
    var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

    function start_lvl(&$output, $depth) {
        $indent = str_repeat("\t", $depth);
        $output .= "\n$indent<ul class=\"sub-menu\">\n";
    }

    function end_lvl(&$output, $depth) {
        $indent = str_repeat("\t", $depth);
        $output .= "$indent</ul>\n";
    }

    function start_el(&$output, $item, $depth, $args) {
        global $wp_query;
        $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, $args ) );
        $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        ) .'"' : '';

        $description  = ! empty( $item->description ) ? esc_attr( strtolower( $item->description )) : '';
        $item_title   = ! empty( $item->attr_title )  ? esc_attr( $item->attr_title ) : '';

        if ( strpos($description, ';') !== false ) {
        $description_array = explode (';', $description);
            $image_name = $description_array[0];
            $image_alt = $description_array[1];
        } else {
            $image_name = $description;
            $image_alt = $item_title;
        }

        $item_output = $args->before;
        $item_output .= '<a'. $attributes .'>';
        $item_output .= $args->link_before .'<img src="'.get_bloginfo('template_url').'/images/skin1/'.$image_name.'" alt="'.$image_alt.'" title="'.$item_title.'" />'.$args->link_after;
        $item_output .= '</a>';
        $item_output .= $args->after;

        $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }

    function end_el(&$output, $item, $depth) {
        $output .= "";
    }

}

Затем скрипт jqDock перехватывает идентификатор меню («menu-first») и заменяет вывод wp_nav_menu на меню Dock. HTML-вывод меню Dock изменяется в зависимости от параметров, указанных при загрузке jqDock.

2. Вопрос:

Я хотел бы не отображать (то есть исключать) определенные пункты меню в зависимости от того, где пользователь находится на сайте. Например, я хотел бы показывать элемент «Домой» только тогда, когда пользователь не находится в «Доме», а элемент «Случайный пост» только тогда, когда он есть.

3. Отклоненные решения:

а. Несколько меню: регистрация и создание нескольких меню, а затем их условный вызов может работать; однако я не думаю, что это идеальное или чистое решение по многим причинам. Кроме того, несколько меню не легко поддерживать или обновлять.

б. Поиск и замена регулярных выражений : это может вынудить меня менять параметр иглы каждый раз, когда я изменяю параметры jqDock, поскольку вывод HTML изменяется.

с. CSS-свойство 'display': скрытие элементов через CSS-свойство display работает, но поскольку оно должно быть применено к выводу меню jqDock, оно влияет на визуальную визуализацию меню.

4. Неудачные решения:

а. Фильтр по wp_nav_menu_items : я попытался перехватить переменную '$ items' (строку) и присвоить ей разные значения через условные теги с помощью следующего кода:

function userf_dynamic_nav_menu ($items) {
    $items_array_home = explode('<a', $items);
    $items_array_nothome = $items_array_home;

    unset($items_array_home[1]);
    unset($items_array_nothome[2]);

    $items_home = implode('<a', $items_array_home);
    $items_nothome = implode('<a', $items_array_nothome);

    if ( is_home() ) {
        $items = $items_home;
    } else {
        $items = $items_nothome;
    }
    return $items;
}
add_filter('wp_nav_menu_first_items', 'userf_dynamic_nav_menu');

Это работает только частично, потому что пункты меню меняются, но условные теги игнорируются. Я думаю, что это имеет смысл из-за момента, когда фильтр применяется.

б. Пользовательская функция меню навигации . Я попытался создать свою собственную функцию меню навигации, чтобы иметь возможность добавить аргумент exclude в массив $ defaults и использовать этот слегка измененный код wp_list_pagesдля заполнения дополнительного аргумента:

$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );

Есть идеи?

Marventus
источник
Можете ли вы показать нам свой пользовательский класс ходунков?
soulseekah
Привет Souleseekah, я только добавил это к своему оригинальному сообщению. Благодарность!
Марвентус
Я тоже думал о передаче excludeаргумента, но, в отличие от wp_list_pagesмногих других функций WP, wp_nav_menuего не включает. Так что даже если я укажу один при вызове меню или в Уокере, его не поймут внутри wp_nav_menu, не так ли?
Марвентус
Извините, я не думал прямо, когда я написал это, удалил сразу.
soulseekah
Не беспокойся об этом!
Марвентус

Ответы:

26

Способ 1

Вы можете добавить конструктор в свой пользовательский Walker для хранения некоторых дополнительных аргументов исключения, таких как:

class custom_nav_walker extends Walker_Nav_Menu {
    function __construct( $exclude = null ) {
        $this->exclude = $exclude;
    }

    function skip( $item ) {
        return in_array($item->ID, (array)$this->exclude);
        // or
        return in_array($item->title, (array)$this->exclude);
        // etc.
    }

    // ...inside start_el, end_el
    if ( $this->skip( $item ) ) return;
}

Или отбросьте конструктор и установите его $excludeсвойство перед тем, как передать его как ходок, wp_nav_menu()например:

$my_custom_nav_walker = new custom_nav_walker;
$my_custom_nav_walker->exclude = array( ... );

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

Способ 2

Вот как вы можете это сделать, подключившись к wp_get_nav_menu_itemsфильтру.

function wpse31748_exclude_menu_items( $items, $menu, $args ) {
    // Iterate over the items to search and destroy
    foreach ( $items as $key => $item ) {
        if ( $item->object_id == 168 ) unset( $items[$key] );
    }

    return $items;
}

add_filter( 'wp_get_nav_menu_items', 'wpse31748_exclude_menu_items', null, 3 );

Примечание: object_idэто объект, на который указывает меню, в то время IDкак идентификатор меню отличается.

Дайте мне знать, что вы думаете.

soulseekah
источник
Благодарность! Это может сработать. Я попробую и дам вам знать.
Марвентус
Я попробовал подход с использованием констант и, независимо от того, что я пытаюсь, я продолжаю получать ошибку «Неправильный тип данных для второго аргумента» для in_arrayфункции. Я делаю что-то неправильно?
Марвентус
$excludeСвойство должно быть массивом. Поэтому убедитесь, что вы передаете массив в конструктор, или посмотрите обновленный код в моем ответе. В частности, приведение типов $this->exclude, на случай, если массив не передан.
soulseekah
Извините за это: у меня была опечатка в моей функции. Я только что попробовал $exclude = array ('4', '7');и использовал слагов, но это не влияет на выход Уокера. Я попробую второй подход и дам вам знать.
Марвентус
Нет, это тоже не сработало. Я думаю, что я с
ума схожу
0

это помогает

$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );

В качестве примера

< ?php wp_nav_menu( array( 'container_class' => 'menu-header', 'theme_location' => 'primary', 'exclude' => '66' ) ); ?>
SAQ
источник
Привет Сак, я забыл упомянуть, что одним из решений, которое не сработало, было создание пользовательской функции nav_menu и добавление этого кода в качестве дополнительного аргумента к значениям по умолчанию функции. К сожалению, это не сработало. Я не пытался включить его в Walker, но я не думаю, что это сработало бы по той же причине, о которой я упоминал выше, в основном из-за того, что wp_nav_menuаргумент «исключить» отсутствует, но я могу ошибаться.
Марвентус
Я обновил свой оригинальный пост, чтобы включить это для ясности.
Марвентус
Что делать, если вы не используете пользовательский ходок, вместо этого вы используете обычное nav_menu и извлекаете элементы с помощью wp_get_nav_menu_items () с вашим собственным изображением
saq
В общем, это был бы хороший обходной путь, но в этом конкретном случае wp_get_nav_menu_itemsон не будет извлекать изображения, поскольку теги img не сохраняются в собственном пользовательском меню (в их поле описания содержатся только их имена, например, «image1.png»). ). Пользовательский ходок - это то, что позволяет мне вставлять теги img в вывод меню.
Марвентус