Как скрыть пункт меню для неавторизованных пользователей (без плагина)
Я хочу скрыть пункт меню, если пользователь не авторизован.
Сейчас я использую код ниже, который делает это с помощью двух отдельных меню, но чтобы избежать дублирования, я хотел бы управлять только одним навигационным меню.
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' );
Возможно ли скрыть только один пункт меню для неавторизованного пользователя, вместо того способа, который я использую сейчас?

Фильтр 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 );

Поскольку @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
, вместо добавления дополнительного класса, как показано выше.

После этого я реализовал это с помощью 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;
}
Спасибо всем за ваши усилия :)

Мне не нравится идея просто скрывать пункты меню с помощью 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;
}

На основе ответа @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;
}

<?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>';
}
?>

Найдите класс или id пункта меню, который вы хотите скрыть.
Предположим, что класс этого меню — logged-in-menu
.
Затем в файле header.php вашей темы перед закрывающим тегом head добавьте следующий код:
<style>
<?php if(! is_user_logged_in() ) : ?>
.logged-in-menu{
display: none;
}
<?php endif; ?>
</style>

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

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