Как скрыть пункт меню для неавторизованных пользователей (без плагина)

31 июл. 2016 г., 12:01:06
Просмотры: 30.5K
Голосов: 4

Я хочу скрыть пункт меню, если пользователь не авторизован.

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

function my_wp_nav_menu_args( $args = '' ) {

    if ( is_user_logged_in() ) { 
        $args['menu'] = 'logged-in'; // меню для авторизованных
    } else { 
        $args['menu'] = 'logged-out'; // меню для неавторизованных
    }

    return $args;
}
add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );

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

3
Комментарии

что вы хотите скрыть из одного меню?

mmm mmm
31 июл. 2016 г. 12:33:00

Ссылку, которая не должна быть видна незарегистрированным пользователям.

Iqbal Mahmud Iqbal Mahmud
31 июл. 2016 г. 13:02:30

Вариант "без плагина" никогда не является необходимым. Такая логика не зависит от вашего дизайна и стилей и должна сохраняться при редизайне сайта. Поместите её в плагин или mu-плагин. Наличие сотен мини-плагинов вместо большого functions.php или одного огромного плагина не замедляет работу вашего сайта.

kaiser kaiser
31 июл. 2016 г. 15:51:15
Все ответы на вопрос 7
1

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

Вот быстрый (непроверенный) пример.

add_filter( 'wp_nav_menu_objects', function( array $items, object $args ) {

    if ( 'someThemeLocation' !== $args->theme_location ) {
        return $items;
    }

    return array_filter( $items, function( $item ) {
        return '/user-specific-thingy' === $item->url 
            && ! is_user_logged_in();
    } );

}, 10, 2 );
31 июл. 2016 г. 15:10:36
Комментарии

Этот код недействителен, потому что wp_nav_menu_objects передаёт array $sorted_menu_items, stdClass $args, а не array $items, array $args

User User
9 мар. 2023 г. 02:28:56
2

Поскольку @chrisguitarguy уже предоставил более чем достойный ответ, пока я писал этот, вот простое дополнение к двум другим ответам.

Возвращаемое значение функции wp_setup_nav_menu() имеет фильтр, который получает $menu_item в качестве единственного параметра — непосредственно перед возвратом значения. Это объект типа \stdClass со следующими public свойствами, которые можно проверять:

  • ID: term_id, если пункт меню представляет собой таксономию.
  • attr_title: Атрибут title элемента ссылки для этого пункта меню.
  • classes: Массив значений атрибута class для элемента ссылки этого пункта меню.
  • db_id: ID в базе данных для этого пункта как объекта nav_menu_item (0, если не существует).
  • description: Описание этого пункта меню.
  • menu_item_parent: ID родительского пункта меню в базе данных (0, если отсутствует).
  • object: Тип исходного объекта, например, "category", "post" или "attachment".
  • object_id: ID исходного объекта, который представляет этот пункт меню (ID для записей, term_id для категорий).
  • post_parent: ID родительского объекта исходного объекта (0, если отсутствует).
  • post_title: Метка "без названия", если пункт меню представляет запись без заголовка.
  • target: Атрибут target элемента ссылки для этого пункта меню.
  • title: Заголовок этого пункта меню.
  • type: Семейство исходных объектов, например, "post_type" или "taxonomy".
  • type_label: Единственное число названия типа этого пункта меню.
  • url: URL, на который указывает этот пункт меню.
  • xfn: XFN-отношение, выраженное в ссылке этого пункта меню.
  • _invalid: Указывает, представляет ли пункт меню объект, который больше не существует.

Простой callback позволит вам использовать условную логику и, возможно, исключить пункт:

add_filter( 'wp_setup_nav_menu', function( \stdClass $item ) {
    # Проверяем условия и помечаем пункт как недействительный
    $item->_invalid = is_user_logged_in() 
        && 'post' === $item->object
        && 'post_type' === $item->type
        # && … любые другие необходимые проверки
    ;

    return $item;
} );

Логика исключения содержится в свойстве _invalid и выполняется функцией _is_valid_nav_menu_item( $item ), которая используется callback'ом при получении пунктов меню. Она применяется внутри array_filter() для сокращения числа пунктов в зависимости от этого флага.


В дополнение к решению @MD Sultan Nasir Uddin: хотя решение только на CSS будет работать, целью должно быть отсутствие этих данных в запросе, запросе к базе данных и конвейере рендеринга. Для полноты ответа вот как это можно сделать: пример использования wp_add_inline_style() для встраивания стилей и синтаксиса heredoc в PHP для удобочитаемости:

<?php
/** Plugin Name: Скрытие пунктов меню для авторизованных пользователей */

# Добавляем класс:
add_filter( 'wp_nav_menu_args', function( Array $args ) {
    if ( is_user_logged_in() )
        $args['menu_class'] .= ' logged-in';
    return $args;
} );

# Добавляем встроенные стили
add_action( 'wp_enqueue_scripts', function() {

    $styles = <<<STYLES
.logged-in .special-item {
    display: none;
}
STYLES;

    wp_add_inline_style( 'custom-style', $styles );
} );

Вы также можете использовать классы body для более точного таргетинга, например, logged-in, вместо добавления дополнительного класса, как показано выше.

31 июл. 2016 г. 15:49:42
Комментарии

Эта штука _invalid весьма удобна.

chrisguitarguy chrisguitarguy
31 июл. 2016 г. 16:11:31

@chrisguitarguy Да, согласен! Я даже не знал о ней, пока ты не опубликовал ответ и я не прочитал исходный код. Единственное, в чём я сейчас не уверен — не должно ли это просто возвращать false, когда нужно что-то отбросить.

kaiser kaiser
31 июл. 2016 г. 17:41:12
1

После этого я реализовал это с помощью CSS nth-child, процедура следующая:

add_action('wp_head','hide_menu');

function hide_menu() { 
    if ( is_user_logged_in() ) {
        //
    } else {
        $output="<style> .menu li:nth-child(3) { display: none; } </style>";
    }
    echo $output;
}

Спасибо всем за ваши усилия :)

8 авг. 2016 г. 09:05:29
Комментарии

Это не сработает, если позиция пункта меню изменится.

Alexander Holsgrove Alexander Holsgrove
4 мар. 2021 г. 13:13:24
0

Мне не нравится идея просто скрывать пункты меню с помощью CSS, так как они всё равно будут видны при использовании инспектора кода на странице. Для тех, кто ищет более безопасный способ удаления пунктов меню, этот код должен помочь:

В этом примере я хочу удалить пункт меню "Загрузки" для всех, кто в данный момент не авторизован.

add_filter( 'wp_nav_menu_objects', 'remove_menu_items' ), 10, 2 );
function remove_menu_items( $items, $args ) {

    // Если нужно удалить несколько пунктов меню, то хорошей практикой будет заранее задать переменную для проверки, чтобы не выполнять одну и ту же проверку многократно.
    $logged_in = is_user_logged_in();

    foreach ( $items as $item_index => $item ) {
        // Удаляем пункт меню "Загрузки", если пользователь не авторизован
        if ( $item->title == 'Downloads' ) {
            if ( !$logged_in ) {
                unset($items[$item_index]);
            }
        }
    }
    return $items;
}
28 июн. 2022 г. 00:29:30
0

На основе ответа @jamio я согласен, что если просто использовать display:none для меню, оно всё равно будет доступно через inspect element. Поэтому я предлагаю решение, в котором можно скрывать элементы навигации на основе их класса, используя classes[0] вместо title. Это позволяет просто добавить класс к меню вместо настройки каждого пункта через код.

 add_filter( 'wp_nav_menu_objects', 'remove_menu_items', 10, 2 );
function remove_menu_items( $items, $args ) {

    $logged_in = is_user_logged_in();

    foreach ( $items as $item_index => $item ) {
        // Удалить пункт меню с классом "hide_when_logout", если пользователь не авторизован
        if ( $item->classes[0] == 'hide_when_logout' ) {
            if ( !$logged_in ) {
                unset($items[$item_index]);
            }
        }
    }
    return $items;
}
23 окт. 2022 г. 14:25:12
2
<?php
// Затем в файле header.php вашей темы перед закрывающим тегом head используйте следующий код
// https://wordpress.stackexchange.com/questions/233667/how-to-hide-an-item-from-a-menu-to-logged-out-users-without-a-plugin
    <?php
 if ( is_user_logged_in() ) {
       $output="<style>.menu-item-8685 {display: none !important;}</style>";
echo $output;
    } else {  
         echo '<script>alert("Добро пожаловать, посетитель")</script>';
    }
     ?>
4 мар. 2021 г. 01:16:59
Комментарии

Это комбинация принятого ответа и ответа Iqbal?

Rup Rup
4 мар. 2021 г. 02:26:13

да, это ответ | и он отлично сработал у меня...

Mohannad Najjar Mohannad Najjar
14 мар. 2021 г. 19:36:07
3
-3

Найдите класс или id пункта меню, который вы хотите скрыть. Предположим, что класс этого меню — logged-in-menu.

Затем в файле header.php вашей темы перед закрывающим тегом head добавьте следующий код:

<style>
<?php if(! is_user_logged_in() ) : ?>
    .logged-in-menu{
        display: none;
    }
<?php endif; ?>
</style>
31 июл. 2016 г. 14:07:08
Комментарии

Это означает, что вы (а) запрашиваете ненужные данные и (б) это необратимо для дочерних тем или плагинов. Вам следует хотя бы использовать колбэк, прикрепленный к wp_head, или правильно регистрировать и подключать стили. Хотя в таком случае это было бы вроде как приемлемым решением, всё же лучше просто отключить эти элементы условно перед запросом элементов из базы данных.

kaiser kaiser
31 июл. 2016 г. 15:03:24

Для тех, кто задумывается об использовании этого, пожалуйста, не делайте так. Используйте правильные хуки меню, как в ответе @chrisguitarguy.

Alexander Holsgrove Alexander Holsgrove
4 мар. 2021 г. 13:16:18

Это полная ерунда. Любой скрипт-кидди всё равно сможет увидеть ваше меню.

User User
9 мар. 2023 г. 02:22:19