Escludere dinamicamente le voci di menu da wp_nav_menu

21 ott 2011, 20:14:45
Visualizzazioni: 29.5K
Voti: 22

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?

9
Commenti

Puoi mostrarci la tua classe child del walker personalizzato?

soulseekah soulseekah
21 ott 2011 20:42:56

Ciao Souleseekah, l'ho appena aggiunta al mio post originale. Grazie!

Marventus Marventus
21 ott 2011 20:52:42

Ho pensato anche di passare un argomento exclude, ma, contrariamente a wp_list_pages e molte altre funzioni di WP, wp_nav_menu non lo include. Quindi anche se lo specificassi quando chiamo il menu o nel walker, non verrebbe preso in considerazione all'interno di wp_nav_menu, giusto?

Marventus Marventus
21 ott 2011 21:08:15

Scusa, non stavo pensando lucidamente quando ho scritto, cancellato immediatamente.

soulseekah soulseekah
21 ott 2011 21:11:02

Non preoccupartene!

Marventus Marventus
21 ott 2011 21:16:34

Ho confermato che i tag condizionali non funzionano sui filtri applicati a wp_nav_menu_items. Questo codice stampa la stessa immagine: function bla ($items) { $image_number = ( is_user_logged_in ) ? '2' : '1'; $items .= ( is_user_logged_in ) ? '<a href="'.wp_logout_url( $_SERVER['REQUEST_URI'] ).'"><img src="'.get_bloginfo('template_url').'/images/skin1/login_'.$image_number.'_normal.png" alt="Log Out" title="Log Out" /></a>' : '<a href="'.wp_login_url( $_SERVER['REQUEST_URI'] ).'"><img src="'.get_bloginfo('template_url').'/images/skin1/login_'.$image_number.'_normal.png" alt="Log in" title="Log in"/></a>';}

Marventus Marventus
22 ott 2011 06:29:59

Devi CHIAMARE la funzione is_user_logged_in() con le parentesi... si sta facendo tardi lì?

soulseekah soulseekah
22 ott 2011 08:21:39

Ho aggiunto un metodo di filtro wp_nav_menu_items alla mia risposta.

soulseekah soulseekah
22 ott 2011 08:41:14

Accidenti... Hahaha. Beh, a mia difesa, devo ammettere che era piuttosto tardi. Ma comunque...

Marventus Marventus
22 ott 2011 15:28:55
Mostra i restanti 4 commenti
Tutte le risposte alla domanda 2
12
41

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.

21 ott 2011 21:08:38
Commenti

Grazie! Potrebbe funzionare. Proverò e ti farò sapere.

Marventus Marventus
21 ott 2011 21:14:18

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?

Marventus Marventus
21 ott 2011 21:41:28

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.

soulseekah soulseekah
21 ott 2011 21:43:56

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.

Marventus Marventus
22 ott 2011 02:20:54

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

Marventus Marventus
22 ott 2011 02:40:03

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.

soulseekah soulseekah
22 ott 2011 08:20:08

Ha senso. Proverò la nuova versione e ti farò sapere.

Marventus Marventus
22 ott 2011 16:25:09

Souseekah, sei un mito: ha funzionato alla perfezione. Grazie mille!

Marventus Marventus
22 ott 2011 20:31:48

Fantastico, felice di essere stato d'aiuto.

soulseekah soulseekah
23 ott 2011 10:30:50

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

Naweed Chougle Naweed Chougle
18 nov 2015 14:25:12

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

Naweed Chougle Naweed Chougle
18 nov 2015 14:32:29

Il metodo 2 ha funzionato bene, incluso il controllo is_admin(). Sono rimasto un po' sorpreso che nascondere il link 'Home' quando si è sulla pagina principale non sia un'opzione standard, ma questo codice e l'uso di is_front_page() mi hanno dato ciò che volevo.

Ron Burk Ron Burk
13 ott 2016 22:28:44
Mostra i restanti 7 commenti
4

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' ) ); ?>
22 ott 2011 03:11:35
Commenti

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.

Marventus Marventus
22 ott 2011 04:35:24

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

Marventus Marventus
22 ott 2011 05:05:48

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

saq saq
22 ott 2011 06:45:59

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.

Marventus Marventus
22 ott 2011 14:53:04