it-swarm.asia

عرض جزء/فرع شجرة القائمة باستخدام wp_nav_menu ()

لدي قائمة محددة في WP مسؤول يشبه هذا:

alt text

أريد أن أكون قادرًا على عرض جميع الروابط الفرعية على الشريط الجانبي عندما أكون في صفحة رئيسية. على سبيل المثال ، إذا كان المستخدم على صفحة "من نحن" ، فأريد أن تظهر قائمة بالارتباطات الأربعة المميزة باللون الأخضر على الشريط الجانبي.

نظرت إلى الوثائق الخاصة بـ wp_nav_menu () ولا يبدو أن لديها أي طريقة مضمنة لتحديد عقدة معينة من قائمة معينة لاستخدامها كنقطة بداية عند إنشاء الروابط.

لقد قمتُ بإنشاء حل لـ موقف مشابه اعتمد على العلاقات التي أنشأها أصل الصفحة ، لكنني أبحث عن حل يستخدم نظام القائمة على وجه التحديد. سيكون موضع تقدير أي مساعدة.

109
jessegavin

كان هذا لا يزال في ذهني ، لذا فقد أعدت النظر فيه ووضعت هذا الحل ، وهذا لا يعتمد على السياق كثيرًا:

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

function submenu_limit( $items, $args ) {

    if ( empty( $args->submenu ) ) {
        return $items;
    }

    $ids       = wp_filter_object_list( $items, array( 'title' => $args->submenu ), 'and', 'ID' );
    $parent_id = array_pop( $ids );
    $children  = submenu_get_children_ids( $parent_id, $items );

    foreach ( $items as $key => $item ) {

        if ( ! in_array( $item->ID, $children ) ) {
            unset( $items[$key] );
        }
    }

    return $items;
}

function submenu_get_children_ids( $id, $items ) {

    $ids = wp_filter_object_list( $items, array( 'menu_item_parent' => $id ), 'and', 'ID' );

    foreach ( $ids as $id ) {

        $ids = array_merge( $ids, submenu_get_children_ids( $id, $items ) );
    }

    return $ids;
}

استعمال

$args = array(
    'theme_location' => 'slug-of-the-menu', // the one used on register_nav_menus
    'submenu' => 'About Us', // could be used __() for translations
);

wp_nav_menu( $args );
73
Rarst

@ goldenapples: لا تعمل فئة Walker الخاصة بك . لكن الفكرة جيدة حقا. لقد صنعت مشية بناءً على فكرتك:

class Selective_Walker extends Walker_Nav_Menu
{
    function walk( $elements, $max_depth) {

        $args = array_slice(func_get_args(), 2);
        $output = '';

        if ($max_depth < -1) //invalid parameter
            return $output;

        if (empty($elements)) //nothing to walk
            return $output;

        $id_field = $this->db_fields['id'];
        $parent_field = $this->db_fields['parent'];

        // flat display
        if ( -1 == $max_depth ) {
            $empty_array = array();
            foreach ( $elements as $e )
                $this->display_element( $e, $empty_array, 1, 0, $args, $output );
            return $output;
        }

        /*
         * need to display in hierarchical order
         * separate elements into two buckets: top level and children elements
         * children_elements is two dimensional array, eg.
         * children_elements[10][] contains all sub-elements whose parent is 10.
         */
        $top_level_elements = array();
        $children_elements  = array();
        foreach ( $elements as $e) {
            if ( 0 == $e->$parent_field )
                $top_level_elements[] = $e;
            else
                $children_elements[ $e->$parent_field ][] = $e;
        }

        /*
         * when none of the elements is top level
         * assume the first one must be root of the sub elements
         */
        if ( empty($top_level_elements) ) {

            $first = array_slice( $elements, 0, 1 );
            $root = $first[0];

            $top_level_elements = array();
            $children_elements  = array();
            foreach ( $elements as $e) {
                if ( $root->$parent_field == $e->$parent_field )
                    $top_level_elements[] = $e;
                else
                    $children_elements[ $e->$parent_field ][] = $e;
            }
        }

        $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );  //added by continent7
        foreach ( $top_level_elements as $e ){  //changed by continent7
            // descend only on current tree
            $descend_test = array_intersect( $current_element_markers, $e->classes );
            if ( !empty( $descend_test ) ) 
                $this->display_element( $e, $children_elements, 2, 0, $args, $output );
        }

        /*
         * if we are displaying all levels, and remaining children_elements is not empty,
         * then we got orphans, which should be displayed regardless
         */
         /* removed by continent7
        if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
            $empty_array = array();
            foreach ( $children_elements as $orphans )
                foreach( $orphans as $op )
                    $this->display_element( $op, $empty_array, 1, 0, $args, $output );
         }
        */
         return $output;
    }
}

الآن يمكنك استخدام:

<?php wp_nav_menu( 
   array(
       'theme_location'=>'test', 
       'walker'=>new Selective_Walker() ) 
   ); ?>

الإخراج عبارة عن قائمة تحتوي على العنصر الجذر الحالي وهو أطفال (وليس أطفالهم). Def: Root element: = عنصر قائمة المستوى العلوي الذي يتوافق مع الصفحة الحالية أو هو أصل الصفحة الحالية أو أحد الوالدين لأحد الوالدين ...

لا يجيب هذا بالضبط على السؤال الأصلي ولكن تقريبًا ، حيث لا يزال هناك عنصر المستوى العلوي. هذا جيد بالنسبة لي ، لأنني أريد عنصر المستوى العلوي كعنوان للشريط الجانبي. إذا كنت ترغب في التخلص من هذا ، فقد تضطر إلى تجاوز display_element أو استخدام HTML-Parser.

14
davidn

مرحبًا @ jessegavin :

يتم تخزين قوائم التنقل في مجموعة من أنواع المنشورات المخصصة والتصنيفات المخصصة. يتم تخزين كل قائمة كمصطلح (أي "حول القائمة" ، الموجود في wp_terms) من تصنيف مخصص (على سبيل المثال ، nav_menu ، موجود في wp_term_taxonomy.)

يتم تخزين كل عنصر من عناصر قائمة التنقل كنشر لـ post_type=='nav_menu_item' (ie "About the Firm" ، الموجود في wp_posts) مع سماته المخزّنة كـ meta post (في wp_postmeta) باستخدام بادئة meta_key حيث _menu_item_* حيث _menu_item_menu_item_parent معرف نشر عنصر عنصر القائمة الرئيسية الأصل في عنصر القائمة.

يتم تخزين العلاقة بين القوائم وعناصر القائمة في wp_term_relationships حيث يرتبط object_id بالرمز $post->ID لعنصر قائمة التنقل و $term_relationships->term_taxonomy_id يتعلق بالقائمة المحددة بشكل جماعي في wp_term_taxonomyand wp_terms.

أنا متأكد من أنه سيكون من الممكن ربط كلاً من 'wp_update_nav_menu' و 'wp_update_nav_menu_item' لإنشاء قوائم فعلية في wp_terms ومجموعة موازية من العلاقات في wp_term_taxonomy و wp_term_relationships حيث يحتوي كل عنصر قائمة التنقل على عناصر قائمة فرعية للتنقل أيضًا يصبح انها قائمة التنقل الخاصة.

كنت ترغب أيضًا في ربط 'wp_get_nav_menus' (التي اقترحت إضافتها إلى WP 3.0 استنادًا إلى بعض الأعمال المماثلة التي كنت أقوم بها قبل بضعة أشهر) لضمان أن قوائم Nav التي تم إنشاؤها لا يتم عرضها للتلاعب بها من قبل المستخدم في المشرف ، وإلا فسيتم مزامنتها بسرعة كبيرة ومن ثم سيكون لديك كابوس بيانات في يدك.

يبدو وكأنه مشروع ممتع ومفيد ، لكنه رمز واختبار أكثر قليلاً مما أستطيع تحمله الآن جزئيًا لأن أي شيء يتزامن مع البيانات يميل إلى أن يكون PITA عندما يتعلق الأمر بالتخلص من جميع الأخطاء (ولأن العملاء المدفوعين يضغطون علي لإنجاز الأمور. :) لكن مع التسلح بالمعلومات أعلاه ، فأنا مطور برنامج المساعد ووردبريس المتحمس جدًا يمكنه تشفيرها إذا أرادوا ذلك.

بالطبع أنت تدرك الآن أنه إذا قمت بتنفيذ الكود ، فأنت ملزم بنشره هنا مرة أخرى حتى نتمكن من الاستفادة من سعادتك! :-)

12
MikeSchinkel

هذا امتداد للمشاة يجب أن يفعل ما تبحث عنه:

class Selective_Walker extends Walker_Nav_Menu
{

    function walk( $elements, $max_depth) {

        $args = array_slice(func_get_args(), 2);
        $output = '';

        if ($max_depth < -1) //invalid parameter
            return $output;

        if (empty($elements)) //nothing to walk
            return $output;

        $id_field = $this->db_fields['id'];
        $parent_field = $this->db_fields['parent'];

        // flat display
        if ( -1 == $max_depth ) {
            $empty_array = array();
            foreach ( $elements as $e )
                $this->display_element( $e, $empty_array, 1, 0, $args, $output );
            return $output;
        }

        /*
         * need to display in hierarchical order
         * separate elements into two buckets: top level and children elements
         * children_elements is two dimensional array, eg.
         * children_elements[10][] contains all sub-elements whose parent is 10.
         */
        $top_level_elements = array();
        $children_elements  = array();
        foreach ( $elements as $e) {
            if ( 0 == $e->$parent_field )
                $top_level_elements[] = $e;
            else
                $children_elements[ $e->$parent_field ][] = $e;
        }

        /*
         * when none of the elements is top level
         * assume the first one must be root of the sub elements
         */
        if ( empty($top_level_elements) ) {

            $first = array_slice( $elements, 0, 1 );
            $root = $first[0];

            $top_level_elements = array();
            $children_elements  = array();
            foreach ( $elements as $e) {
                if ( $root->$parent_field == $e->$parent_field )
                    $top_level_elements[] = $e;
                else
                    $children_elements[ $e->$parent_field ][] = $e;
            }
        }

        $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );

        foreach ( $top_level_elements as $e ) {

            // descend only on current tree
            $descend_test = array_intersect( $current_element_markers, $e->classes );
            if ( empty( $descend_test ) )  unset ( $children_elements );

            $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );
        }

        /*
         * if we are displaying all levels, and remaining children_elements is not empty,
         * then we got orphans, which should be displayed regardless
         */
        if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
            $empty_array = array();
            foreach ( $children_elements as $orphans )
                foreach( $orphans as $op )
                    $this->display_element( $op, $empty_array, 1, 0, $args, $output );
         }

         return $output;
    }

}

بناءً على كود mfields 'الذي أشرت إليه في تعليقي سابقًا. كل ما تفعله هو التحقق عند السير في القائمة لمعرفة ما إذا كان العنصر الحالي هو (1) عنصر القائمة الحالي ، أو (2) سلف لعنصر القائمة الحالي ، وتوسيع الشجرة الفرعية تحتها فقط إذا كان أي من هذه الشروط صحيحًا . أتمنى أن يكون هذا مناسبا لك.

لاستخدامها ، ما عليك سوى إضافة وسيطة "walker" عند الاتصال بالقائمة ، أي:

<?php wp_nav_menu( 
   array(
       'theme_location'=>'test', 
       'walker'=>new Selective_Walker() ) 
   ); ?>
10
goldenapples

أنا وضعت الصف التالي لنفسي. سيعثر على أفضل عنصر تنقل للصفحة الحالية ، أو يمكنك إعطاؤه معرف تنقل أعلى مستهدف في مُنشئ walker.

class Walker_SubNav_Menu extends Walker_Nav_Menu {
    var $target_id = false;

    function __construct($target_id = false) {
        $this->target_id = $target_id;
    }

    function walk($items, $depth) {
        $args = array_slice(func_get_args(), 2);
        $args = $args[0];
        $parent_field = $this->db_fields['parent'];
        $target_id = $this->target_id;
        $filtered_items = array();

        // if the parent is not set, set it based on the post
        if (!$target_id) {
            global $post;
            foreach ($items as $item) {
                if ($item->object_id == $post->ID) {
                    $target_id = $item->ID;
                }
            }
        }

        // if there isn't a parent, do a regular menu
        if (!$target_id) return parent::walk($items, $depth, $args);

        // get the top nav item
        $target_id = $this->top_level_id($items, $target_id);

        // only include items under the parent
        foreach ($items as $item) {
            if (!$item->$parent_field) continue;

            $item_id = $this->top_level_id($items, $item->ID);

            if ($item_id == $target_id) {
                $filtered_items[] = $item;
            }
        }

        return parent::walk($filtered_items, $depth, $args);
    }

    // gets the top level ID for an item ID
    function top_level_id($items, $item_id) {
        $parent_field = $this->db_fields['parent'];

        $parents = array();
        foreach ($items as $item) {
            if ($item->$parent_field) {
                $parents[$item->ID] = $item->$parent_field;
            }
        }

        // find the top level item
        while (array_key_exists($item_id, $parents)) {
            $item_id = $parents[$item_id];
        }

        return $item_id;
    }
}

اتصال Nav:

wp_nav_menu(array(
    'theme_location' => 'main_menu',
    'walker' => new Walker_SubNav_Menu(22), // with ID
));
8
Matt

تحديث: أنا جعلت هذا في البرنامج المساعد. حمل هنا .


كنت بحاجة إلى حل هذا بنفسي وانتهى بي المطاف في كتابة مرشح على نتائج البحث القائمة. يتيح لك استخدام wp_nav_menu كالمعتاد ، ولكن يمكنك اختيار قسم فرعي من القائمة بناءً على عنوان العنصر الأصل. أضف معلمة submenu إلى القائمة مثل ذلك:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => 'About Us',
));

يمكنك حتى الوصول إلى عدة مستويات بعمق بوضع خطوط مائلة في:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => 'About Us/Board of Directors'
));

أو إذا كنت تفضل ذلك باستخدام صفيف:

wp_nav_menu(array(
  'menu' => 'header',
  'submenu' => array('About Us', 'Board of Directors')
));

يستخدم إصدار slug من العنوان ، مما يجعله مسامحا لأشياء مثل العواصم وعلامات الترقيم.

8
Marcus Downing

@ davidnhakre مرحبًا ، لدي حل قبيح بدون محلل HTML أو تجاوز display_element.

 class Selective_Walker extends Walker_Nav_Menu
    {
        function walk( $elements, $max_depth) {

            $args = array_slice(func_get_args(), 2);
            $output = '';

            if ($max_depth < -1) //invalid parameter
                return $output;

            if (empty($elements)) //nothing to walk
                return $output;

            $id_field = $this->db_fields['id'];
            $parent_field = $this->db_fields['parent'];

            // flat display
            if ( -1 == $max_depth ) {
                $empty_array = array();
                foreach ( $elements as $e )
                    $this->display_element( $e, $empty_array, 1, 0, $args, $output );
                return $output;
            }

            /*
             * need to display in hierarchical order
             * separate elements into two buckets: top level and children elements
             * children_elements is two dimensional array, eg.
             * children_elements[10][] contains all sub-elements whose parent is 10.
             */
            $top_level_elements = array();
            $children_elements  = array();
            foreach ( $elements as $e) {
                if ( 0 == $e->$parent_field )
                    $top_level_elements[] = $e;
                else
                    $children_elements[ $e->$parent_field ][] = $e;
            }

            /*
             * when none of the elements is top level
             * assume the first one must be root of the sub elements
             */
            if ( empty($top_level_elements) ) {

                $first = array_slice( $elements, 0, 1 );
                $root = $first[0];

                $top_level_elements = array();
                $children_elements  = array();
                foreach ( $elements as $e) {
                    if ( $root->$parent_field == $e->$parent_field )
                        $top_level_elements[] = $e;
                    else
                        $children_elements[ $e->$parent_field ][] = $e;
                }
            }

            $current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' );  //added by continent7
            foreach ( $top_level_elements as $e ){  //changed by continent7
                // descend only on current tree
                $descend_test = array_intersect( $current_element_markers, $e->classes );
                if ( !empty( $descend_test ) ) 
                    $this->display_element( $e, $children_elements, 2, 0, $args, $output );
            }

            /*
             * if we are displaying all levels, and remaining children_elements is not empty,
             * then we got orphans, which should be displayed regardless
             */
             /* removed by continent7
            if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
                $empty_array = array();
                foreach ( $children_elements as $orphans )
                    foreach( $orphans as $op )
                        $this->display_element( $op, $empty_array, 1, 0, $args, $output );
             }
            */

/*added by alpguneysel  */
                $pos = strpos($output, '<a');
            $pos2 = strpos($output, 'a>');
            $topper= substr($output, 0, $pos).substr($output, $pos2+2);
            $pos3 = strpos($topper, '>');
            $lasst=substr($topper, $pos3+1);
            $submenu= substr($lasst, 0, -6);

        return $submenu;
        }
    }
4
Alp Güneysel

تحقق من الكود في البرنامج المساعد الخاص بي أو استخدمه لغرضك ؛)

يضيف هذا البرنامج المساعد عنصر واجهة مستخدم "قائمة التنقل" المحسّن. يوفر العديد من الخيارات التي يمكن ضبطها لتخصيص إخراج القائمة المخصصة من خلال عنصر واجهة المستخدم.

وتشمل الميزات:

  • التسلسل الهرمي المخصص - "العناصر الفرعية ذات الصلة فقط" أو "العناصر الفرعية ذات الصلة فقط".
  • ابتداء من العمق والحد الأقصى لمستوى العرض + عرض مسطح.
  • عرض جميع عناصر القائمة بدءًا من العنصر المحدد.
  • عرض المسار المباشر فقط للعنصر الحالي أو الأطفال فقط
    العنصر المحدد (خيار لتضمين العنصر الأصل).
  • فئة مخصصة لكتلة القطعة.
  • وتقريبا جميع المعلمات للدالة wp_nav_menu.

http://wordpress.org/extend/plugins/advanced-menu-widget/

3
Ján Bočínec

لقد تقدمت ووكر معدلة من شأنها أن تساعد! ليست مثالية - إنها تترك بعض العناصر الفارغة ، لكنها تفعل الخدعة. التعديل هو بالأساس تلك بتات الامتياز الحالية $. آمل أن يساعد شخص ما!

class Kanec_Walker_Nav_Menu extends Walker {
/**
 * @see Walker::$tree_type
 * @since 3.0.0
 * @var string
 */
var $tree_type = array( 'post_type', 'taxonomy', 'custom' );

/**
 * @see Walker::$db_fields
 * @since 3.0.0
 * @todo Decouple this.
 * @var array
 */
var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' );

/**
 * @see Walker::start_lvl()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param int $depth Depth of page. Used for padding.
 */
function start_lvl(&$output, $depth) {
    $indent = str_repeat("\t", $depth);
    $output .= "\n$indent<ul class=\"sub-menu\">\n";
}

/**
 * @see Walker::end_lvl()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param int $depth Depth of page. Used for padding.
 */
function end_lvl(&$output, $depth) {
    global $current_branch;
    if ($depth == 0) $current_branch = false;
    $indent = str_repeat("\t", $depth);
    $output .= "$indent</ul>\n";
}

/**
 * @see Walker::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
 */
function start_el(&$output, $item, $depth, $args) {
    global $wp_query;
    global $current_branch;

    // Is this menu item in the current branch?
    if(in_array('current-menu-ancestor',$item->classes) ||
    in_array('current-menu-parent',$item->classes) ||
    in_array('current-menu-item',$item->classes)) {
        $current_branch = true; 
    }

    if($current_branch && $depth > 0) {
        $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 ) );
        $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        ) .'"' : '';

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

}

/**
 * @see Walker::end_el()
 * @since 3.0.0
 *
 * @param string $output Passed by reference. Used to append additional content.
 * @param object $item Page data object. Not used.
 * @param int $depth Depth of page. Not Used.
 */
function end_el(&$output, $item, $depth) {
    global $current_branch;
    if($current_branch && $depth > 0) $output .= "</li>\n";
    if($depth == 0) $current_branch = 0;
}

}

3
user2735

يشتمل إخراج قائمة التنقل على الكثير من الفئات للعنصر الحالي ، وأسلاف العنصر الحالي ، إلخ. في بعض الحالات ، تمكنت من فعل ما تريد القيام به من خلال السماح بإخراج شجرة التنقل بالكامل ، ثم استخدام css لتقليصه أطفال فقط من الصفحة الحالية ، إلخ.

3
user3017