Escludere dinamicamente le voci di menu da wp_nav_menu
Ho cercato informazioni su come escludere/rimuovere voci di menu dai menu personalizzati, e l'unico thread che ho trovato non aveva risposte utili per me.
1. Contesto:
Ho creato un menu Dock utilizzando i menu personalizzati di WP (wp_nav_menu) e jqDock sul mio sito. Poiché jqDock necessita di immagini o link di immagini continui per funzionare, sto utilizzando un walker personalizzato in modo che l'output HTML del menu di navigazione appaia così:
<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>
ecc...
</div>
Il codice del mio walker personalizzato è:
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 .= "";
}
}
Lo script jqDock quindi intercetta l'ID del menu ('menu-first') e sostituisce l'output di wp_nav_menu con quello del menu Dock. L'output HTML del menu Dock cambia in base alle opzioni specificate durante il caricamento di jqDock.
2. La Domanda:
Vorrei non visualizzare (cioè escludere) determinate voci di menu in base a dove si trova l'utente nel sito. Ad esempio, vorrei mostrare la voce Home solo quando l'utente non è nella Home, e la voce Post casuale solo quando lo è.
3. Soluzioni Scartate:
a. Menu multipli: Registrare e creare menu multipli e poi chiamarli condizionalmente potrebbe funzionare; tuttavia, non penso che questa sia una soluzione ideale né pulita per molte ragioni. Inoltre, i menu multipli non sono facili da mantenere o aggiornare.
b. Ricerca e Sostituzione con Regex: Questo potrebbe costringermi a cambiare il parametro needle ogni volta che cambio le opzioni di jqDock perché l'output HTML viene modificato.
c. Proprietà CSS 'display': Nascondere gli elementi attraverso la proprietà display CSS funziona, ma poiché deve essere applicata all'output del menu jqDock, influisce sul rendering visuale del menu.
4. Soluzioni Fallite:
a. Filtro per wp_nav_menu_items: Ho provato a catturare la variabile '$items' (stringa) e assegnarle valori diversi attraverso tag condizionali con il seguente codice:
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');
Questo funziona solo parzialmente, perché le voci del menu cambiano, ma i tag condizionali vengono ignorati. Immagino che questo abbia senso a causa del momento in cui viene applicato il filtro.
b. Funzione menu nav personalizzata: Ho provato a creare la mia funzione menu nav personalizzata per poter aggiungere un argomento exclude all'array $defaults e utilizzare questo codice leggermente modificato da wp_list_pages
per popolare l'argomento aggiuntivo:
$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );
Qualche idea?

Metodo 1
Puoi aggiungere un costruttore al tuo Walker personalizzato per memorizzare alcuni argomenti aggiuntivi di esclusione, come:
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);
// oppure
return in_array($item->title, (array)$this->exclude);
// ecc.
}
// ...all'interno di start_el, end_el
if ( $this->skip( $item ) ) return;
}
Oppure elimina il costruttore e imposta la sua proprietà $exclude
prima di passarlo come walker a wp_nav_menu()
in questo modo:
$my_custom_nav_walker = new custom_nav_walker;
$my_custom_nav_walker->exclude = array( ... );
A seconda di ciò che stai escludendo, fornisci la forma corretta all'esclusione.
Metodo 2
Ecco come potresti procedere agganciandoti al filtro wp_get_nav_menu_items
.
function wpse31748_exclude_menu_items( $items, $menu, $args ) {
// Itera sugli elementi per cercare e distruggere
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 );
Nota: object_id
è l'oggetto a cui punta il menu, mentre ID
è l'ID del menu, questi sono diversi.
Fammi sapere cosa ne pensi.

Ho provato l'approccio con il costruttore ma, non importa cosa faccia, continuo a ricevere un errore "Tipo di dato errato per il secondo argomento" per la funzione in_array
. Sto sbagliando qualcosa?

La proprietà $exclude
deve essere un array. Quindi assicurati di passare un array al costruttore, oppure controlla il codice aggiornato nella mia risposta. In particolare il typecast per $this->exclude
, nel caso non venga passato un array.

Mi dispiace: c'era un errore di battitura nella mia funzione. Ho appena provato $exclude = array ('4', '7');
e anche usando gli slug, ma non ha alcun effetto sull'output del walker. Proverò il secondo approccio e ti farò sapere.

No, non ha funzionato neanche quello. Penso di essere esausto dal tentativo di risolvere questo problema, quindi potrebbe influire sulla mia... "performance", :-)

Beh, una cosa che potresti non fare è confrontare il valore effettivo di $item
, ricorda che $item
è una classe standard con proprietà, quindi devi confrontare una delle sue proprietà con qualunque cosa tu stia fornendo. Puoi fare var_dump
di $item
per vedere quali proprietà sono disponibili per il confronto quando escludi. Ho modificato il mio codice per renderlo più chiaro.

Con il metodo 2, le voci del menu scompaiono anche da Aspetto > Menu nella dashboard. Come si evita che ciò accada?

........La risposta è che serve il controllo condizionale is_admin() per verificare se la dashboard è in fase di caricamento.

questo aiuta
$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );
come esempio
<?php wp_nav_menu( array( 'container_class' => 'menu-header', 'theme_location' => 'primary', 'exclude' => '66' ) ); ?>

Ciao Saq, ho dimenticato di menzionare che una delle soluzioni che non ha funzionato era creare una funzione nav_menu personalizzata e aggiungere quel codice come argomento aggiuntivo ai valori predefiniti della funzione. Purtroppo, non ha funzionato. Non ho provato a includerlo nel walker, ma non penso che funzionerebbe neanche per lo stesso motivo che ho menzionato sopra, principalmente perché wp_nav_menu
non ha un argomento "exclude", ma potrei sbagliarmi.

Ho aggiornato il mio post originale per includerlo e chiarire meglio.

E se invece di usare un custom walker, usassi un nav_menu regolare ed estraessi gli elementi con wp_get_nav_menu_items() con la tua immagine personalizzata

Sarebbe una buona soluzione in generale, ma in questo caso specifico, wp_get_nav_menu_items
non recupererà le immagini perché i tag img non sono memorizzati nel menu personalizzato (solo i loro nomi di file sono nel campo descrizione, ad esempio "image1.png"). Il custom walker è ciò che mi permette di inserire i tag img nell'output del menu.
