Excluderea dinamică a elementelor din meniul wp_nav_menu

21 oct. 2011, 20:14:45
Vizualizări: 29.5K
Voturi: 22

Am încercat să caut informații despre cum să exclud/elimin elemente din meniurile personalizate, și singurul thread pe care l-am găsit nu avea răspunsuri utile pentru mine.

1. Context:

Am creat un meniu Dock folosind meniurile personalizate WP (wp_nav_menu) și jqDock pe site-ul meu. Deoarece jqDock are nevoie de imagini continue sau linkuri cu imagini pentru a funcționa, folosesc un walker personalizat astfel încât output-ul HTML al meniului să arate așa:

<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>
etc...
</div>

Codul pentru walker-ul meu personalizat este:

class custom_nav_walker extends Walker_Nav_Menu 
{
    // Tipul arborelui de meniu
    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 .= "";
    }
}

Scriptul jqDock prinde apoi ID-ul meniului ('menu-first') și înlocuiește output-ul wp_nav_menu cu cel al meniului Dock. Output-ul HTML al meniului Dock se modifică în funcție de opțiunile specificate la încărcarea jqDock.

2. Întrebarea:

Aș dori să nu afișez (adică să exclud) anumite elemente din meniu în funcție de locul unde se află utilizatorul pe site. De exemplu, aș dori să afișez elementul Home doar când utilizatorul nu este pe pagina Home, și elementul Post aleatoriu doar când este.

3. Soluții respinse:

a. Meniuri multiple: Înregistrarea și crearea mai multor meniuri și apoi apelarea lor condițional ar putea funcționa; totuși, nu cred că este o soluție ideală sau curată din multe motive. De asemenea, meniurile multiple sunt dificil de întreținut sau actualizat.

b. Căutare și înlocuire Regex: Acest lucru m-ar putea forța să schimb parametrul needle de fiecare dată când schimb opțiunile jqDock deoarece output-ul HTML este modificat.

c. Proprietatea CSS 'display': Ascunderea elementelor prin proprietatea CSS display funcționează, dar deoarece trebuie aplicată output-ului meniului jqDock, afectează redarea vizuală a meniului.

4. Soluții eșuate:

a. Filtru pentru wp_nav_menu_items: Am încercat să prind variabila '$items' (string) și să-i atribui valori diferite prin tag-uri condiționale cu următorul cod:

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');

Aceasta funcționează doar parțial, deoarece elementele meniului se schimbă, dar tag-urile condiționale sunt ignorate. Presupun că acest lucru are sens din cauza momentului în care este aplicat filtrul.

b. Funcție meniu nav personalizată: Am încercat să creez propria funcție de meniu nav personalizată pentru a putea adăuga un argument exclude la array-ul $defaults și pentru a utiliza acest cod ușor modificat din wp_list_pages pentru a popula argumentul suplimentar:

$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );

Aveți idei?

9
Comentarii

Puteți să ne arătați clasa voastră personalizată de tip walker pentru submeniu?

soulseekah soulseekah
21 oct. 2011 20:42:56

Salut Souleseekah, tocmai am adăugat-o în postarea mea originală. Mulțumesc!

Marventus Marventus
21 oct. 2011 20:52:42

M-am gândit să adaug și un argument exclude, dar, spre deosebire de wp_list_pages și multe alte funcții din WP, wp_nav_menu nu include unul asemănător. Deci chiar dacă aș specifica unul când apelez meniul sau în walker, acesta nu ar fi luat în considerare în interiorul funcției wp_nav_menu, nu-i așa?

Marventus Marventus
21 oct. 2011 21:08:15

Scuze, nu m-am gândit clar când am scris asta, am șters imediat.

soulseekah soulseekah
21 oct. 2011 21:11:02

Nu-ți face griji!

Marventus Marventus
21 oct. 2011 21:16:34

Am confirmat că tag-urile condiționale nu funcționează pe filtrele aplicate la wp_nav_menu_items. Acest cod afișează aceeași imagine: 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 oct. 2011 06:29:59

Trebuie să APELI funcția is_user_logged_in() cu parantezele... Ești obosit deja?

soulseekah soulseekah
22 oct. 2011 08:21:39

Am adăugat o metodă de filtrare wp_nav_menu_items în răspunsul meu.

soulseekah soulseekah
22 oct. 2011 08:41:14

La naiba... Hahaha. Ei bine, în apărarea mea, trebuie să recunosc că era destul de târziu. Dar totuși...

Marventus Marventus
22 oct. 2011 15:28:55
Arată celelalte 4 comentarii
Toate răspunsurile la întrebare 2
12
41

Metoda 1

Puteți adăuga un constructor la Walker-ul personalizat pentru a stoca argumente suplimentare de excludere, cum ar fi:

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);
        // sau
        return in_array($item->title, (array)$this->exclude);
        // etc.
    }

    // ...în interiorul start_el, end_el
    if ( $this->skip( $item ) ) return;
}

Sau omiteți constructorul și setați proprietatea $exclude înainte de a-l transmite ca walker la wp_nav_menu() astfel:

$my_custom_nav_walker = new custom_nav_walker;
$my_custom_nav_walker->exclude = array( ... );

În funcție de ce excludeți, furnizați forma corectă pentru excludere.

Metoda 2

Aceasta este modalitatea prin care ați putea realiza acest lucru prin conectarea la filtrul wp_get_nav_menu_items.

function wpse31748_exclude_menu_items( $items, $menu, $args ) {
    // Iterați prin elementele pentru a căuta și elimina
    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 );

Notă: object_id este obiectul la care meniul face referire, în timp ce ID este ID-ul meniului, acestea sunt diferite.

Spuneți-mi părerea dumneavoastră.

21 oct. 2011 21:08:38
Comentarii

Mulțumesc! S-ar putea să funcționeze. O să încerc și vă anunț.

Marventus Marventus
21 oct. 2011 21:14:18

Am încercat abordarea cu constructorul și, indiferent ce fac, primesc constant eroarea "Tip de date incorect pentru al doilea argument" pentru funcția in_array. Fac ceva greșit?

Marventus Marventus
21 oct. 2011 21:41:28

Proprietatea $exclude trebuie să fie un array. Deci asigură-te că transmiți un array în constructor, sau verifică codul actualizat în răspunsul meu. În special conversia de tip pentru $this->exclude, pentru cazul în care nu este transmis un array.

soulseekah soulseekah
21 oct. 2011 21:43:56

Îmi cer scuze pentru asta: am avut o greșeală de tipar în funcția mea. Tocmai am încercat $exclude = array ('4', '7'); și folosind și slug-urile, dar nu are niciun efect asupra rezultatului walker-ului. Voi încerca a doua abordare și vă voi anunța.

Marventus Marventus
22 oct. 2011 02:20:54

Nu, nici asta nu a funcționat. Cred că mi-am epuizat creierul încercând să rezolv asta, așa că s-ar putea să-mi afecteze... "performanța", :-)

Marventus Marventus
22 oct. 2011 02:40:03

Ei bine, un lucru pe care s-ar putea să nu-l faci este să compari valoarea actuală $item, amintește-ți că $item este o clasă standard cu proprietăți, așa că trebuie să compari una dintre proprietățile sale cu orice furnizezi. Poți folosi var_dump pe $item pentru a vedea ce fel de proprietăți sunt disponibile pentru comparare atunci când excludi. Am editat codul meu pentru a-l face mai clar.

soulseekah soulseekah
22 oct. 2011 08:20:08

Are sens. Voi încerca noua versiune și vă voi anunța.

Marventus Marventus
22 oct. 2011 16:25:09

Souseekah, ești genial: a funcționat perfect. Mulțumesc mult!

Marventus Marventus
22 oct. 2011 20:31:48

Grozav, mă bucur că am putut ajuta.

soulseekah soulseekah
23 oct. 2011 10:30:50

Cu metoda 2, elementele de meniu dispar și din Aspect > Meniuri în panoul de administrare. Cum poți evita acest lucru?

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

........Răspunsul este că ai nevoie de verificarea condițională is_admin() pentru a vedea dacă panoul de administrare este încărcat.

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

Metoda 2 a funcționat perfect, inclusiv verificarea is_admin(). Am fost puțin surprins că ascunderea link-ului 'Acasă' când sunt pe pagina principală nu este o opțiune standard, dar acest cod și folosirea is_front_page() mi-au oferit ceea ce doream.

Ron Burk Ron Burk
13 oct. 2016 22:28:44
Arată celelalte 7 comentarii
4

ajută asta

$exclude_array = ( $args->exclude ) ? explode(',', $args->exclude) : array();
$args->exclude = implode( ',', apply_filters('wp_nav_menu_excludes', $exclude_array) );

ca exemplu

<?php wp_nav_menu( array( 'container_class' => 'menu-header', 'theme_location' => 'primary', 'exclude' => '66' ) ); ?>
22 oct. 2011 03:11:35
Comentarii

Salut Saq, am uitat să menționez că una dintre soluțiile care nu a funcționat a fost crearea unei funcții personalizate nav_menu și adăugarea acelui cod ca argument suplimentar la valorile implicite ale funcției. Din păcate, nu a funcționat. Nu am încercat să o includ în walker, dar nu cred că ar funcționa nici asta din același motiv pe care l-am menționat mai sus, mai ales că wp_nav_menu nu are un argument "exclude", dar aș putea greși.

Marventus Marventus
22 oct. 2011 04:35:24

Am actualizat postarea inițială pentru a include acest lucru pentru claritate.

Marventus Marventus
22 oct. 2011 05:05:48

ce-ar fi să nu folosești un walker personalizat, ci în schimb să folosești un nav_menu obișnuit și să extragi elementele cu wp_get_nav_menu_items() cu imaginea ta personalizată

saq saq
22 oct. 2011 06:45:59

Aceasta ar fi o soluție bună în general, dar în acest caz particular, wp_get_nav_menu_items nu va prelua imaginile deoarece tag-urile img nu sunt stocate în meniul personalizat (doar numele fișierelor sunt în câmpul de descriere, de exemplu, "image1.png"). Walker-ul personalizat este ceea ce îmi permite să inserez tag-urile img în output-ul meniului.

Marventus Marventus
22 oct. 2011 14:53:04