web-dev-qa-db-fra.com

Wordpress - Comment savoir si un élément de menu a des enfants?

Je développe un thème wordpress avec des sous-menus imbriqués. Je dois faire les éléments sans enfants visuellement différents de ceux qui ont des enfants. En ce moment, j'ai ce menu, mais cela pourrait changer:

A
  a1
  a2
B
  b1
  b2
C

Comme vous pouvez le constater, A et B ont des enfants. C pas - j'ai besoin que ce soit différent au niveau CSS.

Idéalement, j'aimerais avoir une classe has-children en A et B, mais pas en C.

Jusqu'à présent, j'ai réussi à créer une classe "Menu Walker" PHP que je peux instancier et transmettre à wp_nav_menu . Son constructeur ressemble à ceci:

class My_Walker_Nav_Menu extends Walker_Nav_Menu {
  function start_el(&$output, $item, $depth, $args) {
    ...
    if(??? $item has children???) {
      // I know what to do here
    }
  }
}

Alors, comment puis-je savoir si $item a des enfants ou est une feuille?

EDIT: cette question a été répondue par une personne appelée "keesiemeijer" sur les forums Wordpress. Je laisse cette prime expirée au cas où il voudrait la récupérer. Sinon, je marquerai ma propre réponse comme valide.

21
kikito

J'ai demandé dans le forum WordPress et keesiemeijer m'a pointé vers cet autre post , dans lequel ils ont fait ce qui suit:

Au lieu de modifier start_el, ils ont modifié display_element en ajoutant les deux lignes suivantes (lignes 37-38 ici ):

//display this element (THESE ARE NOT THE LINES)
if ( is_array( $args[0] ) )
  $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

// THESE TWO ARE THE LINES:               
if( ! empty( $children_elements[$element->$id_field] ) )
  array_Push($element->classes,'parent');

J'ai laissé les deux lignes précédentes comme référence spatiale et aussi comme commentaire pour d'autres réponses dans ce post. Il semble que wordpress "essaie" de définir une propriété "has_children" dans $args, mais c’est soit de le faire mal, soit de manière que je ne comprends pas. Dans tous les cas, ce paramètre has_children n'est jamais transmis à start_el (voir l'exemple var_dump d'un $ args ici )

Cela pourrait être un bogue sur la version Wordpress que j'ai (3.2.1) et pourrait avoir été corrigé dans la version la plus récente.

Dans tous les cas, la réponse que j’ai eue sur le forum Wordpress est celle qui m’a aidé à résoudre le problème, j’estime donc que cela est réglé. J'attendrai que la prime expire au cas où keesiemeijer voudrait mettre sa réponse ici.

6
kikito

Ajoutez ceci à functions.php cela ajoutera la classe 'dropdown' aux parents

Un nouveau moyen d'améliorer la performance

function menu_set_dropdown( $sorted_menu_items, $args ) {
    $last_top = 0;
    foreach ( $sorted_menu_items as $key => $obj ) {
        // it is a top lv item?
        if ( 0 == $obj->menu_item_parent ) {
            // set the key of the parent
            $last_top = $key;
        } else {
            $sorted_menu_items[$last_top]->classes['dropdown'] = 'dropdown';
        }
    }
    return $sorted_menu_items;
}
add_filter( 'wp_nav_menu_objects', 'menu_set_dropdown', 10, 2 );

Ancien: intensif sur la DB  

add_filter( 'nav_menu_css_class', 'check_for_submenu', 10, 2);
function check_for_submenu($classes, $item) {
    global $wpdb;
    $has_children = $wpdb->get_var("SELECT COUNT(meta_id) FROM wp_postmeta WHERE meta_key='_menu_item_menu_item_parent' AND meta_value='".$item->ID."'");
    if ($has_children > 0) array_Push($classes,'dropdown'); // add the class dropdown to the current list
    return $classes;
}
32
janw

Simple vous utilisez cette façon:

Expliquez: Je crée un menu avec "walker":

$walker = new Nav_Walker;
wp_nav_menu(array(
        'container'=>'nav',
        'container_class'=>'menu',
        'menu_class'=>'list-unstyled list-inline',
        'walker'=>$walker
    ));

Classe Walker:

class Nav_Walker extends Walker_Nav_Menu
{ 
      public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0)
    {
        if($args->walker->has_children)
        {
            //code...
        }   
    }
}

Nous avons un objet 'walker', vous pouvez var_dump ($ args) pour voir plus de choses . J'utilise pour mon projet!

14
starrynight.1406

Il semble que le problème ait finalement été résolu. La dernière version bêta de Wordpress depuis l’écriture actuelle 4.0 a mis à jour la classe Walker_Nav_Menu et ajouté une propriété $has_children.

/**
 * Whether the current element has children or not.
 *
 * To be used in start_el().
 *
 * @since 4.0.0
 * @access protected
 * @var bool
 */
protected $has_children;

Nous n’avons donc plus besoin de pirater function display_element(...).

9
Jorge Bucaran
class My_Walker_Nav_Menu extends Walker_Nav_Menu {
  function start_el(&$output, $item, $depth, $args) {
    ...
    if($args['has_children']) {
      // I know what to do here
    }
  }
}
3
Eugene Manuilov

La réponse ci-dessus de Kikito fait le tour, mais pas de la manière la plus réutilisable. À mon avis, la meilleure approche est la suivante:

function display_element($element, &$children_elements, $max_depth, $depth=0, $args, &$output) {
    // the first few lines of the method...

    //display this element; handle either arrays or objects gracefully
    if ( is_array( $args[0] ) )
        $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );

    elseif ( is_object($args[0]) )
        $args[0]->has_children =  ! empty( $children_elements[$element->$id_field] );

    // the rest of the method...
}

Remplacer Walker::display_element() est la bonne solution, mais il est préférable de s'attaquer réellement au problème à la racine du problème plutôt que de simplement se concentrer sur une classe pour l'instant, pour deux raisons. Tout d’abord, le vrai problème n’est pas une classe manquante, mais bien le bogue non corrigé dans WordPress noté par Kikito: le problème est que $args[0] n’est pas toujours un tableau. Cela semble être le type généralement attendu de Walker::display_element(), mais nous traitons en fait de Walker_Nav_Menu::display_element() et dans ce cas, args finit par être transmis en tant que type d'objet standard plutôt qu'en tant que type de tableau. En tant que tel, nous devons simplement ajouter l'élément has_children en utilisant la notation objet au lieu de la notation tableau. Problème résolu! [1]

L'ajout de ces noms elseif pour le cas ordinaire du menu Navigation. C'est le même formulaire qui, espérons-le, en fera la classe principale de ce correctif , auquel cas vous n'aurez plus besoin de l'étendre. Ils devraient probablement le corriger davantage pour expliquer le cas où $args[0] n'est ni un tableau ni un objet, mais je ne m'attends pas à ce que cela se produise.

Deuxièmement, afin de bien séparer les problèmes entre les différentes méthodes, les classes doivent vraiment être ajoutées dans la méthode start_el() ou ailleurs, car display_element() ne fait rien de la gestion de classe.

En conséquence, vous pouvez ensuite remplacer start_el() comme bon vous semble: vous pouvez ajouter vos propres classes personnalisées, ou ignorer entièrement des éléments, ou fournir du texte personnalisé, ou ce que vous préférez. (Dans mon cas, je travaille sur une implémentation de menu Javascript existante qui a des exigences de classification très spécifiques basées sur les parents et les enfants, donc je ne peux pas simplement ajouter les mêmes classes à tout ce qui a un enfant - ce qui est précisément pourquoi cette séparation des problèmes est importante.) Dans mon code:

function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
    $indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
    $class_names = $value = '';

    $classes = empty( $item->classes ) ? array() : (array) $item->classes;
    $classes[] = 'menu-item-' . $item->ID;

    $has_children = (is_object($args) && $args->has_children) || (is_array($args) &&  $args['has_children']);
    if ($has_children) {
        // do whatever you need to do
    }

    // everything else the method does...
}

[1] C’est bien sûr l’un des pièges potentiels des langages à typage dynamique comme PHP ... Ce n’est pas un problème, tant que vous êtes prudent. Les développeurs WordPress ne faisaient pas attention ici.

3
Chris Krycho

Si vous ne voulez pas la surcharge d'une requête ou d'une fonction difficile, vous pouvez le faire dans jQuery:

(function() {
    // Add 'has_children' class to menus
    jQuery.each(jQuery('.menu-item').has('ul.sub-menu'), function() {
        jQuery(this).addClass('has_children');
    });
})();
2
Zachary Schuessler

Ajoutez ceci à votre functions.php

add_filter('wp_nav_menu_objects', 'menu_has_children', 10, 2);

function menu_has_children($sorted_menu_items, $args) {
    $parents = array();
    foreach ( $sorted_menu_items as $key => $obj )
            $parents[] = $obj->menu_item_parent;
    foreach ($sorted_menu_items as $key => $obj)
        $sorted_menu_items[$key]->has_children = (in_array($obj->ID, $parents)) ? true : false;
    return $sorted_menu_items;
}

Ensuite, dans votre lecteur, vous pourrez vérifier si $ item-> has_children est vrai ou faux

2
Joakim Ling
    /**
     * @see My_Nav_Walk::start_el()
     * @since 3.0.0
     *
     * @param string $output Passed by reference. Used to append additional content.
     * @param object $item Menu item data object.
     * @param int $depth Depth of menu item. Used for padding.
     * @param int $current_page Menu item ID.
     * @param object $args
     * @url:http://www.liyinqing.com
     */
class My_Nav_Walk extends Walker_Nav_Menu {
    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        ) .'"' : '';

    // Check our custom has_children property.here is the points
    if ( $args->has_children ) {
      $attributes .= ' class="menu parent"';
    }

    $item_output = $args->before;
    $item_output .= '<a'. $attributes .'>';
    $item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
    $item_output .= '</a>';
    $item_output .= $args->after;

    $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
  }

  function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
    $id_field = $this->db_fields['id'];
    if ( is_object( $args[0] ) ) {/.here is the points
      $args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
    }
    return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
  }

}

1
liying

Étant donné que cette question est l’un des premiers résultats de la recherche Google, elle a été référencée dans d’autres pages Web et la plupart des réponses fournies ici sont périmées. Je pensais publier cette réponse.

start_el () a classes dans l'objet $ item. Donc, vous pouvez ajouter ceci à start_el ():

   if(in_array('menu-item-has-children', $item->classes) && $depth != 0)
   {
       // Your Code
   }

Notez que $ depth n’est pas requis dans les conditions, mais s’il est supprimé, votre code sera appliqué au premier élément (c’est-à-dire un élément de profondeur 0).

Pour ce qui est de la compatibilité, la classe 'menu-item-has-children' a été ajoutée à WordPress 3.7 (octobre 2013) et je l’ai testée sur le dernier WordPress 4.4 (à ce jour de poster cette réponse).

1
Kalimah Apps

utiliser ce code simple dans votre classe de marcheur

 class Description_Walker extends Walker_Nav_Menu
    {
    function start_el(  &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    global $wp_query;
    ////

    ur code in this part 
    ///////
     $depth_classes = array(
        ( $depth == 0 ? 'nav-item' : 'nav-submenu-item' ),
        ( $depth >=2 ? 'sub-sub-menu-item' : '' ),
        ( $depth % 2 ? 'menu-item-odd' : 'menu-item-even' ),
        'menu-item-depth-' . $depth
    );
    $depth_class_names = esc_attr( implode( ' ', $depth_classes ) );

    // passed classes
    $classes = empty( $item->classes ) ? array() : (array) $item->classes;
    $class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );


     $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
    }
    }
0
john

Merci pour la fonction Start_el, ma fonction suit cette fonction pour exécuter la requête. J'ai une fonction comptera le sous-menu du menu parent par ID.

function nav_count_children($parent_id){
    global $wpdb;
    $query = "SELECT COUNT(*) FROM $wpdb->postmeta 
            WHERE meta_key='_menu_item_menu_item_parent' 
            AND meta_value=$parent_id";
    $count_children = $wpdb->get_var( $query );
    return $count_children;
}

Run Dans chaque fonction de la fonction wp_get_nav_menu_items, sélectionnez l'ID du menu parent par $ item-> menu_item_parent == 0.

Cela fonctionne pour moi et très simple.

0
D. Lân Nguyễn