在编写了可以在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插件中心。我确定可以对插件进行一些改进,如果您认为有任何改进,请留下评论并告诉我。
我还用过Walker_Page类生成了动态菜单。使用Walker_Page方法的好处是,它可以用于覆盖WordPress随附的默认页面小部件。