首页 > 解决方案 > wp_list_pages 和页面层次结构的问题

问题描述

我正在尝试做一些我确信应该非常简单的事情,wp_list_pages 但我无法让它可靠地工作。

我有一组页面,它们都使用我创建的自定义页面模板,lesson-plans-page.php.

我正在尝试在侧边栏中输出使用此模板的所有页面的分层(正确嵌套)菜单。

目前只有5页。1 个父页面,4 个子页面

但是,它仅在对页面进行排序以使父页面排在首位时才分层wp_list_pages工作...不应该自动分层输出它们还是我遗漏了什么?

我首先创建了父页面,所以如果我按它排序的ID话。如果我不按任何排序,它会输出与父页面处于同一级别的所有 5 个页面。我注意到它仍然知道父级有子级,因为它附加到它的类,<li>但是子级没有嵌套在<ul></ul>父级下面。

这是实际的页面结构和名称:

-Inclusive Learning Through Drama
--Early Stage 1
--Stage 1
--Stage 2
--Stage 3

我的代码就是这个(它有效,但只是因为 sort_column.

<ul>
    <?php wp_list_pages( array( 
        'post_type' => 'page',
        'meta_key' => '_wp_page_template',
        'meta_value' => 'lesson-plans-page.php',
        'hierarchical' => true,
        'title_li' => '',
        'sort_column' => 'ID',
        'link_before' => '<button class="expand-children"></button>'
        ) ); ?>
</ul>

总的来说,它的输出是这样的,这就是我想要的(为了简单起见,我删除了<a>and<buttons>标签)

<ul>
    <li class="page_item_has_children">Inclusive Learning Through Drama
        <ul class=children>
            <li>Early Stage 1</li>
            <li>Stage 1</li>
            <li>Stage 2</li>
            <li>Stage 3</li>
        </ul>
    </li>
</ul>

如果我删除 sort_column 它会像这样输出

<ul>
    <li>Early Stage 1</li>
    <li>Stage 1</li>
    <li>Stage 2</li>
    <li>Stage 3</li>
    <li class="page_item_has_children">Inclusive Learning Through Drama</li>
</ul>

所以,我的问题是:如何让它输出正确嵌套的 html,而不添加一些基于已知因素(如 ID)的排序(如果我碰巧以不同的顺序创建它们,这将不起作用)?

我尝试过的其他事情是在页面上使用 WP order 属性,但这不起作用,除非我在 1 处开始子页面上的订单并且在 0 处开始父页面上的订单,这与拥有父子关系的观点背道而驰。(子页面顺序独立于父页面顺序)。

用户应该能够以任何顺序创建新页面并让它们自动添加到侧边栏菜单中。

标签: wordpress

解决方案


好的,所以我继续使用递归函数和一些其他逻辑解决了这个问题。

这比预期的要复杂得多。

问题wp_list_pages是它必须按照父子关系存在的确切顺序进行排序才能正常工作。或者,您可以一次运行 1 级,但这必须基于特定的给定页面。即您所在的页面,而不仅仅是在每个结果上迭代。这在我的情况下不起作用,因为我正在创建一个基于使用特定模板的所有页面的菜单。中的depth论点wp_list_pages也没有什么误导性……或者至少,我误解了它的作用。它不会,如果设置为 1,则仅获取第一级...它根据找到的第一页的任何级别获取 1 级...这对我的场景没有帮助,因为我不知道找到的第一页是什么..为此,您需要首先知道最顶部的页面是什么..

以下是我的完美解决方案。

总之;

  • 自定义查询使用我的模板返回所有页面
  • 结果循环
  • 递归函数向下迭代每个分支

结果图片

<?php
// this function recursivly loops down each branch
function buildTree(array $page_ids, $parent_page_id = 0, $current_page = 0) {

    $branch = '';

    // loop each page id in the array
    foreach($page_ids as $page_id) {

        // if the looped page's parent matches the parent page passed in, add it to the branch
        if (wp_get_post_parent_id($page_id) == $parent_page_id) {

            // pass each page back in using the current page in the loop as the parent argument to check for grandchildren... etc
            $children = buildTree($page_ids, $page_id, $current_page);
            
            // create html markup for the branch. 
            $branch .= '<li class="' .'page_item_'. $page_id . ' child_of_' . $parent_page_id . ($children !== '' ? ' page_item_has_children':'') . ($page_id == $current_page ? ' current_page_item':'') .'">';
            $branch .= '<a href="'. get_permalink($page_id) .'"' . ($page_id == $current_page ? 'aria-current="page"':'') . '>';
            $branch .= '<button class="expand-children"></button>';
            $branch .= esc_html( get_the_title($page_id) );
            $branch .= '</a>';

            // if children were returned append them inside the <li>
            if ($children) {
                $branch .= $children;
            }

            $branch .= '</li>';
        }

    }

    // if the branch is not empty wrap it in ul tags
    if($branch) {
        $branch = '<ul class="children">' . $branch .'</ul>';
    }
    
    // return the branch
    // will be a nested ul li ul ... list of all children and grandchildren for the parent passed in
    return $branch;
}

// get the ID of the page we on as well as all it's ancestors. 
// Used for CSS purposes to match current page to selected menu item
// Used for JS purposes to expand/collapse appropriatly. 
$current_page_id = get_the_id();
$current_page_ancestors = get_post_ancestors( $current_page_id );

// Query pages using the lesson plan template
$query = new WP_Query(
    array(
        'post_type' => 'page',
        'meta_key' => '_wp_page_template',
        'meta_value' => 'lesson-plans-page.php',
        'posts_per_page'=> -1,
        'orderby'  => 'menu_order',
        'order' => 'ASC'
    )
);

// loop the results 
// This loops - across - the returned ids and passes ID's with children to the recursive function which itterates - down - that branch
if ( $query->have_posts() ) {

    $nav = '';
    $top_parent = 0;
    $processed = [];
    $page_ids = wp_list_pluck($query->posts, 'ID');

    while ( $query->have_posts() ) {
        $query->the_post();

        // check if the pages parent is in the query results (it may have a parent, but not in the list)
        $parent_listed = in_array( $post->post_parent , $page_ids );

        // if the parent is not in the list it's a 'top level' page
        // top level pages only need to be handled separately if they dont have children, as those with children will be captured here when the child id is processed
        if( $parent_listed ) {
            
            // returns all ancestors. The top most one last in the array
            // we want to find the most senior ancestor that is also in the list
            $ancestors = get_post_ancestors( $post->ID );
            

            foreach($ancestors as $ancestor) {

                // returns false or key of listed ancestor
                $ancestor_listed = array_search( $ancestor , $page_ids );

                // the top level parent will be the last one looped 
                // must use strict comparison as key 0 is falsey. 
                if ($ancestor_listed !== false) {
                    // updates as the most senior ancestor listed is found
                    $top_parent = $page_ids[$ancestor_listed];
                }

            }

            // check if the top_parent has been processed before
            $parent_processed = in_array( $top_parent , $processed );

            // only process each top most parent once
            if ( !$parent_processed ) {

                // run the top parent through the recursive function
                // returns all children and grandchildren... of the top_parent
                $children = buildTree($page_ids, $top_parent, $current_page_id);
                
                // create html markup for parent item
                $nav .= '<li class="' .'page_item_'. $top_parent . ($children !== '' ? ' page_item_has_children':'') . ($top_parent == $current_page_id ? ' current_page_item':'') . (in_array($top_parent, $current_page_ancestors) ? ' current_page_ancestor':'') .'">';
                $nav .= '<a href="'. get_permalink($top_parent) .'"' . ($top_parent == $current_page_id ? 'aria-current="page"':'') . '>';
                $nav .= '<button class="expand-children"></button>';
                $nav .= get_the_title($top_parent);
                $nav .= '</a>';

                if ($children) {
                    // if there are children append them
                    $nav .= $children;
                }

                $nav .= '</li>';

                // add parent to processed array to keep track of them
                $processed[] = $top_parent;
                $top_parent = 0;

            }

        } else { 
            // if the page does not have a parent in the list, it is a top level item.
            // however we only need to handle it separatly if it does not have children in the list
            
            // check if the page has children. 
            // They may be in the list in which case it will be captured above. 
            $child_pages = get_pages( array('child_of'  => $post->ID) );

            if( $child_pages ) {

                $child_pages_listed = false;

                foreach( $child_pages as $page ) {

                // if it has children when need to check if they are in the list. 
                // They may not be if they have a different page template. 
                $is_listed = in_array( $page->ID , $page_ids );

                    if( $is_listed ) {

                        $child_pages_listed = $is_listed;
                    }

                }

                // if the page has children but they are not in the list, capture them here. 
                if( !$child_pages_listed ) {

                    $nav .= '<li class="' .'page_item_'. $post->ID . ($post->ID == $current_page_id ? ' current_page_item':'') . (in_array($post->ID, $current_page_ancestors) ? ' current_page_ancestor':'') .'">';
                    $nav .= '<a href="'. get_permalink($post->ID) .'"' . ($post->ID == $current_page_id ? 'aria-current="page"':'') . '>';
                    $nav .= '<button class="expand-children"></button>';
                    $nav .= esc_html( get_the_title() );
                    $nav .= '</a>';
                }

            } else { // if it does not have children then it is a top level page not capture above

                $nav .= '<li class="' .'page_item_'. $post->ID. ($post->ID == $current_page_id ? ' current_page_item':'') . (in_array($post->ID, $current_page_ancestors) ? ' current_page_ancestor':'') .'">';
                $nav .= '<a href="'. get_permalink($post->ID) .'"' . ($post->ID == $current_page_id ? 'aria-current="page"':'') . '>';
                $nav .= '<button class="expand-children"></button>';
                $nav .= esc_html( get_the_title() );
                $nav .= '</a>';
            }
            
        }

    }

    // if the branch is not empty wrap it in ul tags
    if($nav) {
        $nav = '<ul>' .$nav .'</ul>';
    }

    echo $nav;


    /* Restore original Post Data */
    wp_reset_postdata();

} else {
    // no posts found
}

 ?>


推荐阅读