Wordpress - 我怎么知道菜单项是否有孩子?

问题描述 投票:22回答:13

我正在开发一个带有嵌套子菜单的wordpress主题。我需要让没有孩子的元素与有孩子的元素在视觉上不同。现在我有这个菜单,但可能会改变:

A
  a1
  a2
B
  b1
  b2
C

如你所见,A和B有孩子。 C没有 - 我需要它在CSS级别上有所不同。

理想情况下,我想在A和B中有一个has-children类,但在C中没有。

到目前为止,我已经设法创建了一个“Menu Walker”PHP类,我可以实例化并传递给wp_nav_menu。它的构造函数如下所示:

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
    }
  }
}

那么,我如何判断$item是否有孩子,或者是叶子?

编辑:这个问题是由Wordpress论坛中名为“keesiemeijer”的人回答的。我要离开这个赏金过期以防他想要收回它。否则,我会将自己的答案标记为有效。

php wordpress
13个回答
32
投票

将此添加到functions.php,它会将'dropdown'类添加到父级

性能更好的新方法

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

旧:数据库密集

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;
}

0
投票

感谢Start_el函数,我的函数遵循该函数来运行查询。我有一个功能将按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;
}

运行在wp_get_nav_menu_items函数的foreach中,通过$ item-> menu_item_parent == 0选择父菜单的ID。

它对我有用而且非常简单。


0
投票

在walker类中使用这个简单的代码

 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
投票

由于这个问题是谷歌搜索的第一个结果之一,已在其他网页中被引用,这里的大部分答案已经过时,我想我会发布这个答案。

start_el()将类作为$ item对象的一部分。所以你可以在start_el()中添加它:

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

请注意,条件中不需要$ depth,但如果删除,您的代码将应用于第一个项目(即具有0深度的项目)。

至于兼容性部分,类'menu-item-has-children'已添加到WordPress 3.7 (October 2013),我已经在最新的WordPress 4.4上测试了它(截至发布此答案时)。


-1
投票

有一个简单的解决方案source

function start_el(&$output, $item, $depth=0, $args=array()) {

  global $wpdb;
  $children_count = $wpdb->get_var(
    $wpdb->prepare("
      SELECT COUNT(*) FROM $wpdb->postmeta
      WHERE meta_key = %s
      AND meta_value = %d
    ", '_menu_item_menu_item_parent', $item->ID)
  );

  if( $children_count > 0 ) {
    // has children
    $classes[] = 'parent';
  }

  [...]

14
投票

你用这种方式很简单:

说明:我用“walker”创建菜单:

$walker = new Nav_Walker;
wp_nav_menu(array(
        'container'=>'nav',
        'container_class'=>'menu',
        'menu_class'=>'list-unstyled list-inline',
        'walker'=>$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...
        }   
    }
}

我们有对象'walker',你可以通过var_dump($ args)来查看更多内容。我正在使用我的项目!


9
投票

似乎问题终于得到了解决。截至当前写作4.0的最新Wordpress测试版更新了Walker_Nav_Menu类并添加了$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;

所以,我们不再需要破解function display_element(...)了。


6
投票

我在WordPress forum和keesiemeijer问我this other post,他们做了以下事情:

他们没有修改start_el,而是修改了display_element,添加了以下两行(37-38行here):

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

我将前两行留作空间参考,并作为对此帖中其他答案的评论。似乎wordpress正在“尝试”在$args中设置'has_children'属性,但它要么做错了,要么以我不理解的方式做错。在任何情况下,has_children参数永远不会传递给start_el(参见$ args here的示例var_dump)

这可能是我得到的Wordpress版本(3.2.1)上的一个错误,可能已在最新版本中修复。

在任何情况下,我在Wordpress论坛中得到的答案是帮助我解决它的答案,所以我认为这已经解决了。我会等到赏金到期,以防keesiemeijer想把答案放在这里。


3
投票
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
投票

Kikito的answer above完成了这个技巧,但不是以最可重复使用的方式。在我看来,更好的方法是这样的:

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...
}

覆盖Walker::display_element()是正确的举措,但最好是在问题的根源上实际解决问题,而不是简单地在此处修改一个类,原因有两个。首先,真正的问题不是缺少类,而是Kikito指出的WordPress中未修补的错误:问题是$args[0]并不总是一个数组。这似乎是Walker::display_element()的典型预期类型,但我们实际上在这里处理Walker_Nav_Menu::display_element(),在这种情况下,args最终作为标准对象类型而不是数组类型传入。因此,我们只需要使用对象表示法而不是数组表示法添加has_children元素。问题解决了![1]

elseif添加到普通Nav Menu案例中。这是一个相同的形式,有望成为this patch的核心类,此时你将不再需要扩展它。他们应该进一步修补它以解释$args[0]既不是数组也不是对象的情况,但我不希望看到这种情况发生。

其次,为了在各种方法之间保持良好的关注点分离,应该在start_el()方法或其他地方添加类,因为display_element()没有进行任何类处理。

因此,您可以根据需要覆盖start_el():您可以添加自己的自定义类,或完全忽略元素,或提供自定义文本或任何您喜欢的内容。 (在我的情况下,我正在解决现有的Javascript菜单实现,该实现具有基于父母和孩子的非常具体的分类要求,所以我不能只为有孩子的所有东西添加相同的类 - 这正是这种分离的原因关注事项。)在我的代码中:

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]这当然是像PHP这样的动态类型语言的潜在缺陷之一......只要你小心,这不是问题。 WordPress开发人员在这里并不小心。


2
投票

如果您不想要硬查询或函数的开销,可以在jQuery中执行此操作:

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

2
投票

将其添加到您的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;
}

然后在你的步行者中你可以检查$ item-> has_children是真还是假


1
投票
    /**
     * @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 );
  }

}

© www.soinside.com 2019 - 2024. All rights reserved.