Come nascondere un elemento del menu agli utenti non loggati (senza plugin)
Voglio nascondere un elemento del menu se un utente non è loggato.
Attualmente sto usando il codice qui sotto che ottiene questo risultato usando due menu separati, ma per evitare duplicazioni, vorrei dover gestire un solo menu di navigazione.
function my_wp_nav_menu_args( $args = '' ) {
if ( is_user_logged_in() ) {
$args['menu'] = 'logged-in'; // menu per utenti loggati
} else {
$args['menu'] = 'logged-out'; // menu per utenti non loggati
}
return $args;
}
add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );
È possibile nascondere solo un elemento specifico per un utente non loggato, invece di farlo nel modo in cui lo sto facendo attualmente?

Filtra wp_nav_menu_objects
. Contiene la lista ordinata degli elementi del menu di navigazione da renderizzare. Dai un'occhiata a wp_setup_nav_menu_item
per alcune proprietà che puoi utilizzare.
Ecco un esempio rapido (non testato).
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 );

Come @chrisguitarguy ha già fornito una risposta più che valida mentre stavo scrivendo questa risposta, ecco un semplice aggiunta alle altre due risposte.
Il valore di return
della funzione wp_setup_nav_menu()
ha un filtro, che riceve $menu_item
come unico valore fornito – esattamente prima che venga restituito – ed è di tipo object
e un \stdClass
con le seguenti proprietà public
che puoi verificare:
ID
: Il term_id se la voce di menu rappresenta un termine di tassonomia.attr_title
: L'attributo title dell'elemento link per questa voce di menu.classes
: L'array dei valori dell'attributo class per l'elemento link di questa voce di menu.db_id
: L'ID del database di questa voce come oggetto nav_menu_item, se esiste (0 se non esiste).description
: La descrizione di questa voce di menu.menu_item_parent
: L'ID del database del nav_menu_item che è il genitore di questa voce, se presente. 0 altrimenti.object
: Il tipo di oggetto originariamente rappresentato, come "category", "post" o "attachment".object_id
: L'ID del database dell'oggetto originale che questa voce di menu rappresenta, ad esempio ID per i post e term_id per le categorie.post_parent
: L'ID del database dell'oggetto genitore dell'oggetto originale, se presente (0 altrimenti).post_title
: Un'etichetta "no title" se la voce di menu rappresenta un post che non ha un titolo.target
: L'attributo target dell'elemento link per questa voce di menu.title
: Il titolo di questa voce di menu.type
: La famiglia di oggetti originariamente rappresentata, come "post_type" o "taxonomy".type_label
: L'etichetta singolare usata per descrivere questo tipo di voce di menu.url
: L'URL a cui punta questa voce di menu.xfn
: La relazione XFN espressa nel link di questa voce di menu._invalid
: Se la voce di menu rappresenta un oggetto che non esiste più.
Quindi un semplice callback ti permetterà di usare della logica condizionale e poi magari escludere una voce:
add_filter( 'wp_setup_nav_menu', function( \stdClass $item ) {
# Verifica le condizioni e invalida una voce se necessario
$item->_invalid = is_user_logged_in()
&& 'post' === $item->object
&& 'post_type' === $item->type
# && … qualsiasi altra condizione necessaria per invalidare una voce
;
return $item;
} );
La logica di esclusione risiede nella proprietà _invalid
ed è eseguita dalla funzione _is_valid_nav_menu_item( $item )
che è un callback usato quando le voci di menu sono recuperate. La usa all'interno di un array_filter()
per ridurre il numero di voci in base a questo flag.
Come estensione alla soluzione di @MD Sultan Nasir Uddin: Sebbene una soluzione solo CSS funzioni, l'obiettivo dovrebbe essere quello di non avere nemmeno i dati in questa richiesta, nella query del database e nel pipeline di rendering. Per una risposta completa, ecco comunque il come: Esempio usando wp_add_inline_style()
per incorporare gli stili e la sintassi heredoc di PHP per leggibilità:
<?php
/** Plugin Name: Nascondi voci di menu per utenti loggati */
# Aggiungi classe:
add_filter( 'wp_nav_menu_args', function( Array $args ) {
if ( is_user_logged_in() )
$args['menu_class'] .= ' logged-in';
return $args;
} );
# Aggiungi stili inline
add_action( 'wp_enqueue_scripts', function() {
$styles = <<<STYLES
.logged-in .special-item {
display: none;
}
STYLES;
wp_add_inline_style( 'custom-style', $styles );
} );
Potresti probabilmente usare le classi del body
per trovare una classe logged-in
o simile per un target più specifico – invece di aggiungere una classe aggiuntiva come sopra.

Dopo averlo realizzato utilizzando il selettore CSS nth-child, la procedura è la seguente:
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;
}
Grazie a tutti per il vostro impegno :)

Non mi piace l'idea di nascondere semplicemente gli elementi del menu con il CSS poiché sarebbero comunque visibili utilizzando l'ispezione del codice sulla pagina. Per chi cerca un modo più sicuro per rimuovere gli elementi del menu, questo dovrebbe funzionare:
In questo esempio voglio rimuovere la voce del menu "Download" per chi non è attualmente loggato.
add_filter( 'wp_nav_menu_objects', 'remove_menu_items' ), 10, 2 );
function remove_menu_items( $items, $args ) {
// Se devi rimuovere più voci di menu, è buona pratica impostare una variabile per il controllo in anticipo in modo che non esegua ripetutamente la stessa funzione di controllo.
$logged_in = is_user_logged_in();
foreach ( $items as $item_index => $item ) {
// Rimuovi la voce "Download" quando non si è loggati
if ( $item->title == 'Downloads' ) {
if ( !$logged_in ) {
unset($items[$item_index]);
}
}
}
return $items;
}

Basandomi sulla risposta di @jamio, concordo sul fatto che se usi semplicemente display:none
per il menu, questo rimane comunque accessibile tramite inspect element
. Per questo motivo ho trovato una soluzione: se vuoi nascondere i menu di navigazione in base alla loro classe, puoi usare classes[0]
invece di title
, in modo da poter semplicemente aggiungere una classe al menu invece di impostare ogni menu tramite codice.
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 ) {
// Rimuove la voce di menu con la classe "hide_when_logout" quando non si è loggati
if ( $item->classes[0] == 'hide_when_logout' ) {
if ( !$logged_in ) {
unset($items[$item_index]);
}
}
}
return $items;
}

<?php
//Poi nel file header.php del tuo tema prima del tag di chiusura head usa il codice seguente
//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("Benvenuto visitatore")</script>';
}
?>

Trova la classe o l'ID della voce del menu che vuoi nascondere.
Supponiamo che la classe di quel menu sia logged-in-menu
Quindi nel file header.php del tuo tema, prima del tag di chiusura head, utilizza il seguente codice:
<style>
<?php if(! is_user_logged_in() ) : ?>
.logged-in-menu{
display: none;
}
<?php endif; ?>
</style>

Ciò significa che stai (a) interrogando dati non necessari e (b) è non reversibile per temi figlio o plugin. Potresti almeno voler utilizzare una callback collegata a wp_head
o registrare e accodare correttamente gli stili. Sebbene sarebbe una soluzione in qualche modo accettabile, sarebbe comunque preferibile disattivare condizionalmente quegli elementi prima di interrogarli dal database.

Per chiunque stia pensando di usare questo metodo, per favore non fatelo. Utilizzate i proper hook dei menu come nella risposta di @chrisguitarguy.
