WordPress动态页面菜单导航

在编写了可以在Wordpress中创建给定页面的子页面列表的函数之后,我需要更强大,更自动的功能。为此,我创建了一个插件,该插件将创建一个包含动态创建的页面菜单的小部件。

该小部件可以确定当前正在显示的页面,并将爬到页面树上,直到找到根页面为止。在攀爬页面树的同时,插件将保留当前所选页面的路径,并且在打印出该树时,路径将被打开。它最适合具有坚实层次结构页面的站点,而不是简单的博客站点。

在效率方面,我已经使用最多嵌套25个级别的页面进行了测试,页面负载只有很小的减少。但是,对于一般的小型Wordpress网站而言,此插件是完美的,因为页面仅嵌套了几层。

<?php
/**
 * Plugin Name: Page Menu Navigation
 * Plugin URI: http://www.hashbangcode.com/
 * Description: Adds an intelligent page navigation menu that is dependent on page hierarchy.
 * Version: 1.0
 * Author: Philip Norton
 * Author URI: http://www.hashbangcode.com/
 */
 
/**
 * Print out hirachical page structure.
 *
 * @global object $post The current post.
 */
function printPages()
{
    global $post;
 
    if ($post->post_type == 'page') {
        if ($post->post_parent > 0) {
            $root  = findPathInformation($post);
            $pages = traversePageTree($root['root'], $root['activepath'], $root['depth']);
        } else {
            $root = $post;
            $pages = traversePageTree($root, array($root->ID), 1);
        }
        echo printPageTree($pages);
    }
}
 
/**
 * From a single page find out how deep it is
 *
 * @param object $page The current page.
 *
 * @return array An array of information about the page and the path.
 */
function findPathInformation($page)
{
    // 上那棵树,看看那是什么。
    $reverse_tree = climbPageTree($page);
    // 整理树以获取当前的活动路径。
    $activePath = flattenTree($reverse_tree);
 
    // 确保当前页面在活动路径中。
    $activePath[] = $page->ID;
 
    $root = $reverse_tree[0];
 
    // 设置为2,就好像我们在此代码中一样,位于根目录下。
    $depth = 2;
 
    // Recursivley遍历页面并找到根页面和深度。
    while (is_array($root->post_parent)) {
       ++$depth;
       $root = $root->post_parent[0];
    }
 
    return array('root' => $root, 'depth' => $depth, 'activepath' => $activePath);
}
 
/**
 * Flatten the tree into a single array.
 *
 * @param array $tree A multi dimensional array of pages.
 *
 * @return array A single dimensional array of pages.
 */
function flattenTree($tree)
{
    $flat = array();
 
    while (is_array($tree[0]->post_parent)) {
       $flat[] = $tree[0]->ID;
       $tree = $tree[0]->post_parent;
    }
 
    $flat[] = $tree[0]->ID;
 
    return $flat;
}
 
/**
 * Find out if the current page is in the active path.
 *
 * @param integer $id The ID of the current page.
 * @param array $activePath An array of ID's of the pages in the current path.
 *
 * @return boolean True if the page is in current path, otherwise false.
 */
function inActivePath($id, $activePath)
{
    if (in_array($id, $activePath)) {
        return true;
    } else {
        return false;
    }
}
 
/**
 * Starting with the current page go up level after level until the root page
 * reached. This function will run one SQL query for every level.
 *
 * @global wpdb $wpdb The current Wordpress database connection object.
 *
 * @param object $page The current page.
 *
 * @return <type>
 */
function climbPageTree($page)
{
    global $wpdb;
    $parent = $wpdb->get_results("SELECT ID, post_title, post_parent FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'page' AND ID = " . $page->post_parent . " ORDER BY menu_order, post_title", OBJECT);
 
    if (count($parent) > 0) {
        foreach ($parent as $item => $par) {
            if ($par->post_parent != 0) {
                $parent_parent = climbPageTree($par);
                if ($parent_parent !== false) {
                    $parent[$item]->post_parent = $parent_parent;
                }
            } else {
                // 到达树的顶部
                return $parent;
            }
        }
    }
 
    return $parent;
}
 
/**
 * Traverse the page structure and create a tree of the pages.
 *
 * @global wpdb $wpdb The current Wordpress database connection object.
 *
 * @param object $page The page to start the tree traversal from, usually root.
 * @param array $activePath An array of page ID's in the active path.
 * @param integer $maxdepth The maximum depth to follow the traversal
 * @param integer $depth The current depth of the traversal.
 *
 * @return array A tree of pages.
 */
function traversePageTree($page, $activePath = array(), $maxdepth = 10, $depth = 0)
{
    if ($depth >= $maxdepth) {
        // 我们已经达到最大深度,停止遍历。
        return array();
    }
 
    // 获取Wordpress db对象。
    global $wpdb;
 
    $children = $wpdb->get_results("SELECT ID, post_title, post_parent FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'page' AND post_parent = " . $page->ID . " ORDER BY menu_order, post_title", OBJECT);
    if (count($children) > 0) {
        foreach ($children as $item => $child) {
            if (inActivePath($child->ID, $activePath)) {
                // 当前页面处于活动路径中,找到子代。
                $children[$item]->children = traversePageTree($child, $activePath, $maxdepth, $depth + 1);
            }
        }
    }
 
    return $children;
}
 
/**
 * Print out the page tree as created by the traversePageTree() function.
 *
 * @see traversePageTree()
 *
 * @param array $pages A tree of pages.
 */
function printPageTree($pages)
{
    $class = '';
    $output = '';
    $output .= "\n<ul>\n";
    foreach ($pages as $page) {
        if (is_page($page->ID) === true) {
            $class = '';
        }
        $output .=  "<li" . $class . "><a href=\"" . get_page_link($page->ID) . "\" title=\"" . $page->post_title . "\">" . $page->post_title . "</a>";
        $class = '';
        if (isset($page->children) && count($page->children) > 0) {
            $output .= printPageTree($page->children);
        }
        $output .=  "</li>\n";
    }
    $output .=  "</ul>\n";
    return $output;
}
 
/**
 * Widget function
 *
 * @param array $args
 */
function pageMenuNavigationWidget($args)
{
    extract($args);
 
    echo '<div id="subNav">';
    echo printPages();
    echo '</div>';
}
 
register_sidebar_widget(__('Page Menu Navigation'), 'pageMenuNavigationWidget');
$wp_registered_widgets[sanitize_title('Page Menu Navigation')]['description'] = 'Creates a navigation menu.';

如果您喜欢这个插件并且觉得它有用,请告诉我,我将其添加到Wordpress插件中心。我确定可以对插件进行一些改进,如果您认为有任何改进,请留下评论并告诉我。

更新01/11/2010

我还用过Walker_Page类生成了动态菜单。使用Walker_Page方法的好处是,它可以用于覆盖WordPress随附的默认页面小部件。