Cómo ocultar un elemento del menú a usuarios no conectados (sin plugin)
Quiero ocultar un elemento del menú si un usuario no está conectado.
Actualmente estoy usando el siguiente código que logra esto usando dos menús separados, pero para evitar la duplicación, me gustaría tener que gestionar un solo menú de navegación.
function my_wp_nav_menu_args( $args = '' ) {
if ( is_user_logged_in() ) {
$args['menu'] = 'conectado';
} else {
$args['menu'] = 'no-conectado';
}
return $args;
}
add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );
¿Es posible ocultar solo un elemento para un usuario no conectado, en lugar de hacerlo de la manera en que lo hago actualmente?
La solución alternativa sería usar el filtro `wp_nav_menu_objects` para modificar los elementos del menú según el estado de inicio de sesión del usuario. Aquí está un ejemplo:function ocultar_elemento_menu($items) {
if (!is_user_logged_in()) {
// ID del elemento del menú que deseas ocultar
$menu_item_id = 123;
foreach ($items as $key => $item) {
if ($item->ID == $menu_item_id) {
unset($items[$key]);
}
}
}
return $items;
}
add_filter('wp_nav_menu_objects', 'ocultar_elemento_menu');

Filtra wp_nav_menu_objects
. Contendrá la lista ordenada de elementos del menú de navegación para renderizar. Echa un vistazo a wp_setup_nav_menu_item
para ver algunas propiedades que puedes utilizar.
Aquí tienes un ejemplo rápido (no probado).
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 );

Como @chrisguitarguy ya había agregado una respuesta más que válida mientras yo escribía esta respuesta, aquí hay un simple agregado a las otras dos respuestas.
El valor de retorno de la función wp_setup_nav_menu()
tiene un filtro, el cual recibe $menu_item
como único valor proporcionado – exactamente antes de ser retornado – y es de tipo object
y un \stdClass
con las siguientes propiedades public
que puedes verificar:
ID
: El term_id si el elemento del menú representa un término de taxonomía.attr_title
: El atributo título del elemento de enlace para este elemento del menú.classes
: El array de valores del atributo clase para el elemento de enlace de este elemento del menú.db_id
: El ID de base de datos de este elemento como objeto nav_menu_item, si existe (0 si no existe).description
: La descripción de este elemento del menú.menu_item_parent
: El ID de base de datos del nav_menu_item que es el padre del menú de este elemento, si lo hay. 0 en caso contrario.object
: El tipo de objeto representado originalmente, como "category", "post" o "attachment".object_id
: El ID de base de datos del objeto original que representa este elemento del menú, ej. ID para posts y term_id para categorías.post_parent
: El ID de base de datos del objeto padre del objeto original, si lo hay (0 en caso contrario).post_title
: Una etiqueta "sin título" si el elemento del menú representa un post que carece de título.target
: El atributo target del elemento de enlace para este elemento del menú.title
: El título de este elemento del menú.type
: La familia de objetos representados originalmente, como "post_type" o "taxonomy".type_label
: La etiqueta singular utilizada para describir este tipo de elemento del menú.url
: La URL a la que apunta este elemento del menú.xfn
: La relación XFN expresada en el enlace de este elemento del menú._invalid
: Indica si el elemento del menú representa un objeto que ya no existe.
Así que un simple callback te permitirá usar algo de lógica condicional y luego quizás excluir un elemento:
add_filter( 'wp_setup_nav_menu', function( \stdClass $item ) {
# Verificar condiciones e invalidar un elemento en caso
$item->_invalid = is_user_logged_in()
&& 'post' === $item->object
&& 'post_type' === $item->type
# && … lo que necesites verificar para tu invalidación de un elemento
;
return $item;
} );
La lógica de exclusión vive dentro de la propiedad _invalid
y es ejecutada por la función _is_valid_nav_menu_item( $item )
que es un callback utilizado cuando se recuperan los elementos del menú de navegación. La usa dentro de un array_filter()
para reducir el número de elementos dependiendo de esta bandera.
Como extensión a la solución de @MD Sultan Nasir Uddin: Si bien una solución solo con CSS funcionará, el objetivo debería ser no tener siquiera los datos en esta solicitud, en la consulta de base de datos y en la tubería de renderizado. Para una respuesta completa, aquí está el cómo: Ejemplo usando wp_add_inline_style()
para incluir estilos en línea y la sintaxis heredoc de PHP para legibilidad:
<?php
/** Plugin Name: Ocultar elementos del menú para usuarios logueados */
# Agregar clase:
add_filter( 'wp_nav_menu_args', function( Array $args ) {
if ( is_user_logged_in() )
$args['menu_class'] .= ' logged-in';
return $args;
} );
# Agregar estilos en línea
add_action( 'wp_enqueue_scripts', function() {
$styles = <<<STYLES
.logged-in .special-item {
display: none;
}
STYLES;
wp_add_inline_style( 'custom-style', $styles );
} );
Probablemente también podrías usar las clases del body
para encontrar una clase logged-in
o similar para un objetivo más específico – en lugar de agregar una clase adicional como arriba.

Después de eso lo hice usando el selector nth-child de CSS. El procedimiento es:
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;
}
Gracias a todos por su esfuerzo :)

No me gusta la idea de simplemente ocultar elementos del menú con CSS, ya que seguirían siendo visibles usando el inspector de código en la página. Para cualquiera que busque una forma más segura de eliminar elementos del menú, esto debería funcionar:
En este ejemplo quiero eliminar un elemento del menú "Descargas" para cualquier persona que no haya iniciado sesión.
add_filter( 'wp_nav_menu_objects', 'remove_menu_items' ), 10, 2 );
function remove_menu_items( $items, $args ) {
// Si necesitas eliminar varios elementos del menú, es una buena práctica establecer una variable para la verificación de antemano para que no ejecute la misma función de verificación repetidamente.
$logged_in = is_user_logged_in();
foreach ( $items as $item_index => $item ) {
// Eliminar el elemento del menú "Descargas" cuando no se ha iniciado sesión
if ( $item->title == 'Downloads' ) {
if ( !$logged_in ) {
unset($items[$item_index]);
}
}
}
return $items;
}

Basándome en la respuesta de @jamio, estoy de acuerdo en que si simplemente usas display:none
en el menú, todavía es accesible a través de inspeccionar elemento
. Por lo tanto, propongo una solución para ocultar los menús de navegación basados en su clase, utilizando classes[0]
en lugar de title
. De esta manera, puedes simplemente agregar una clase al menú en lugar de configurar cada menú mediante código.
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 ) {
// Elimina el elemento del menú con la clase "hide_when_logout" cuando no está conectado
if ( $item->classes[0] == 'hide_when_logout' ) {
if ( !$logged_in ) {
unset($items[$item_index]);
}
}
}
return $items;
}

<?php
// Luego en el archivo header.php de tu tema, antes de la etiqueta de cierre del head, usa el siguiente código
// 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("Bienvenido visitante")</script>';
}
?>

Encuentra la clase o el ID del elemento del menú que deseas ocultar.
Supongamos que la clase de ese menú es logged-in-menu
Luego, en el archivo header.php de tu tema, antes de cerrar la etiqueta head, utiliza el siguiente código:
<style>
<?php if(! is_user_logged_in() ) : ?>
.logged-in-menu{
display: none;
}
<?php endif; ?>
</style>

Esto significa que (a) estás consultando datos innecesarios y (b) no es reversible para temas hijos o plugins. Deberías al menos usar un callback vinculado a wp_head
o registrar y encolar los estilos correctamente. Aunque entonces sería una solución aceptable, seguiría siendo preferible simplemente desactivar esos elementos condicionalmente antes de consultarlos de la base de datos.

Para cualquiera que esté pensando en usar esto, por favor no lo hagas. Usa los hooks de menú apropiados como en la respuesta de @chrisguitarguy.
