Подтвердить что ты не робот

Wordpress - как узнать, есть ли в элементе меню дети?

Я разрабатываю тему wordpress с вложенными подменю. Мне нужно сделать элементы без детей, визуально отличающихся от тех, у которых есть дети. Прямо сейчас у меня есть это меню, но это может измениться:

A
  a1
  a2
B
  b1
  b2
C

Как вы можете видеть, у A и B есть дети. C не - мне нужно, чтобы он отличался на уровне CSS.

В идеале я хотел бы иметь класс has-children в и B, но не в C.

До сих пор мне удалось создать PHP-класс "Walker", который я могу создать и передать в 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 дочерние элементы или это лист?

EDIT: на этот вопрос ответил кто-то, называемый "keesiemeijer" на форумах Wordpress. Я оставляю эту награду истек, на случай, если он захочет ее вернуть. В противном случае, я буду отмечать свой собственный ответ как действительный.

4b9b3361

Ответ 1

Я спросил на форуме WordPress, и keesiemeijer указал мне на этот другой пост, в котором они сделали следующее:

Вместо изменения start_el они изменили display_element, добавив следующие две строки (строки 37-38 здесь):

//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 "пытается" установить свойство "has_children" в $args, но это либо делает это неправильно, либо каким-то образом я не понимаю. В любом случае этот параметр has_children никогда не передается до start_el (см. Пример var_dump $args здесь)

Это может быть ошибка в версии Wordpress, которую я получил (3.2.1), и может быть исправлена ​​в самой последней версии.

В любом случае, ответ, который я получил на форуме Wordpress, - это тот, который помог мне исправить это, поэтому я считаю, что это улажено. Я буду ждать, пока щедрость истечет на случай, если keesiemeijer захочет поставить свой ответ здесь.

Ответ 2

Добавьте это в 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;
}

Ответ 3

Просто вы используете этот способ:

Объясните: Я создаю меню с помощью "walker":

$walker = new Nav_Walker;
wp_nav_menu(array(
        'container'=>'nav',
        'container_class'=>'menu',
        'menu_class'=>'list-unstyled list-inline',
        'walker'=>$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) видеть больше вещей. Я использую для своего проекта!

Ответ 4

Кажется, что проблема наконец решена. Последняя бета-версия Wordpress от текущей записи 4.0 обновила класс 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(...).

Для реализации подкласса Walker_Nav_Menu, который я написал с использованием встроенной архитектуры фильтра Wordpress, посмотрите здесь.

Ответ 5

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

Ответ 6

Kikito ответить выше получает трюк, но не в самом многоразовом варианте. На мой взгляд, лучший подход таков:

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() - это правильный ход, но лучше решить проблему в корне проблемы, а не просто навязать класс на этом этапе по двум причинам. Во-первых, реальная проблема - это не недостающий класс, а не исправленная ошибка в WordPress, которую заметил Кикито: проблема в том, что $args[0] не всегда является массивом. Кажется, это типично ожидаемый тип для Walker::display_element(), но мы на самом деле имеем дело с Walker_Nav_Menu::display_element(), и в этом случае args заканчивается тем, что передается как стандартный тип объекта, а не тип массива. Таким образом, нам просто нужно добавить элемент has_children, используя нотацию объектов вместо обозначения массива. Проблема решена! [1]

Добавление того, что elseif учитывает обычный случай Nav Menu. Это та же самая форма, которая, мы надеемся, превратит ее в основной класс в этот патч, после чего вам больше не придется его расширять, Вероятно, они должны исправлять его, чтобы учесть случай, когда $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 не были здесь осторожны.

Ответ 7

Если вы не хотите накладных расходов на трудный запрос или функцию, вы можете сделать это в jQuery:

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

Ответ 8

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

}

Ответ 9

Поскольку этот вопрос является одним из первых результатов поиска Google, он упоминается на других веб-страницах, и большинство ответов здесь устарели. Я думал, что опубликую этот ответ.

start_el() имеет классы как часть объекта $item. Поэтому вы можете добавить это в start_el():

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

Обратите внимание, что $depth не требуется в условиях, но если вы удалите, ваш код будет применен к первому элементу (т.е. элемент с глубиной 0).

Что касается части совместимости, то класс 'menu-item-have-children' был добавлен в WordPress 3.7 (октябрь 2013), и я тестировал его на последнем WordPress 4.4 (на момент публикации этого ответа).

Ответ 10

Добавьте это в свои функции. 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 истинным или ложным

Ответ 11

Спасибо за функцию Start_el, моя функция выполняет эту функцию для запуска запроса. У меня функция будет подсчитывать подменю родительского меню по идентификатору.

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 В foreach функции wp_get_nav_menu_items выберите ID родительского меню по $item- > menu_item_parent == 0.

Это работает для меня и очень просто.

Ответ 12

используйте этот простой код в вашем классе 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 );
    }
    }

Ответ 13

Существует простое решение источник.

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

  [...]