WordPress wp_list_pages
:递归结构与Walker类中的层级渲染逻辑
大家好,今天我们来深入探讨WordPress函数 wp_list_pages
,特别是它在处理页面层级结构以及利用 Walker
类实现层级渲染时的核心逻辑。 wp_list_pages
是一个非常常用的函数,用于生成页面列表,它能够自动处理页面之间的父子关系,并将其以嵌套的HTML列表形式呈现。理解其背后的递归机制和 Walker
类的工作方式对于定制化页面列表输出至关重要。
wp_list_pages
的基本用法与参数
首先,让我们回顾一下 wp_list_pages
的基本用法和常用参数:
<?php
$args = array(
'depth' => 0,
'show_date' => '',
'date_format' => get_option('date_format'),
'child_of' => 0,
'exclude' => '',
'exclude_tree' => '',
'include' => '',
'meta_key' => '',
'meta_value' => '',
'authors' => '',
'sort_column' => 'menu_order, post_title',
'sort_order' => '',
'link_before' => '',
'link_after' => '',
'walker' => '',
'echo' => 1,
'title_li' => __('Pages'),
'number' => '',
'post_type' => 'page',
'post_status' => 'publish'
);
wp_list_pages( $args );
?>
这里是一些关键参数的说明:
depth
: 指定显示的层级深度。0
表示显示所有层级。-1
也显示所有层级。1
只显示顶级页面,2
显示顶级页面和一级子页面,以此类推。child_of
: 只显示指定页面ID下的子页面。exclude
: 排除显示的页面ID列表,以逗号分隔。include
: 只显示指定的页面ID列表,以逗号分隔。sort_column
: 排序的依据,默认为menu_order, post_title
,表示先按菜单顺序排序,再按标题排序。walker
: 一个Walker
类的实例,用于定制化输出。这是我们今天讨论的重点。echo
: 是否直接输出结果。1
表示输出,0
表示返回字符串。title_li
: 列表的标题。默认为 ‘Pages’。如果设置为空字符串''
,则不显示标题。
递归结构:_wp_page_stylesheet
函数
wp_list_pages
内部依赖于 _wp_page_stylesheet
函数来构建页面层级结构。虽然这个函数不是直接暴露给开发者使用的,但理解它的工作原理有助于我们更好地理解 wp_list_pages
。
_wp_page_stylesheet
的主要作用是为页面添加 CSS 类,以便于样式化。它通过递归的方式遍历页面树,并根据页面的深度和层级关系添加相应的类。
以下是 _wp_page_stylesheet
的简化逻辑(省略了部分与样式相关的代码):
function _wp_page_stylesheet( $page, $depth = 0, $current_page = false ) {
static $first = true;
$css_class = array( 'page_item', 'page-item-' . $page->ID );
if ( $current_page ) {
$_current_page = get_post( $current_page );
if ( ! empty( $_current_page ) ) {
if ( $page->ID == $current_page ) {
$css_class[] = 'current_page_item';
} elseif ( $_current_page->post_parent == $page->ID ) {
$css_class[] = 'current_page_ancestor';
} elseif ( in_array( $page->ID, get_post_ancestors( $_current_page ) ) ) {
$css_class[] = 'current_page_ancestor';
}
}
} elseif ( get_option('page_for_posts') == $page->ID ) {
$css_class[] = 'current_page_parent';
}
if ( $first ) {
$css_class[] = 'first';
$first = false;
}
$css_class = apply_filters( 'page_css_class', $css_class, $page, $depth, $current_page );
echo ' class="' . implode( ' ', $css_class ) . '"';
}
这个函数的核心在于递归调用自身,以便遍历整个页面树。它会根据当前页面与目标页面之间的关系,添加相应的 CSS 类,例如 current_page_item
、current_page_ancestor
等。
Walker
类:定制化页面列表输出的核心
Walker
类是 WordPress 中用于遍历和输出树状结构的通用类。 wp_list_pages
函数允许我们通过 walker
参数传入一个自定义的 Walker
类实例,从而完全控制页面列表的输出。
Walker
类定义了一系列方法,用于处理树状结构中的每个节点:
start_lvl( &$output, $depth = 0, $args = array() )
: 在遍历到一个节点的子节点列表之前调用。通常用于输出<ul>
标签。end_lvl( &$output, $depth = 0, $args = array() )
: 在遍历完一个节点的子节点列表之后调用。通常用于输出</ul>
标签。start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 )
: 在遍历到一个节点时调用。用于输出列表项的内容,例如<li>
标签和链接。end_el( &$output, $item, $depth = 0, $args = array() )
: 在遍历完一个节点之后调用。用于输出列表项的结束标签,例如</li>
标签。
要定制化 wp_list_pages
的输出,我们需要创建一个继承自 Walker_Page
(或者 Walker
,如果需要更底层的控制) 的类,并重写这些方法。
创建自定义 Walker
类
以下是一个简单的自定义 Walker
类的例子,它会在每个页面链接前添加一个图标:
class My_Custom_Page_Walker extends Walker_Page {
function start_el( &$output, $page, $depth = 0, $args = array(), $id = 0 ) {
if ( $depth ) {
$indent = str_repeat( "t", $depth );
} else {
$indent = '';
}
$css_class = array( 'page_item', 'page-item-' . $page->ID );
if ( isset( $args['pages'] ) ) {
$pages = $args['pages'];
} else {
$pages = array();
}
if ( ! empty( $pages ) && in_array( $page->ID, $pages ) ) {
$css_class[] = 'current_page_item';
} elseif ( get_option('page_for_posts') == $page->ID ) {
$css_class[] = 'current_page_parent';
}
$args['has_children'] = ! empty( $args['walker']->has_children );
$args['current_page'] = get_the_ID();
$css_class = apply_filters( 'page_css_class', $css_class, $page, $depth, $args['current_page'] );
$output .= $indent . '<li class="' . implode( ' ', $css_class ) . '">';
$attributes = '';
if ( ! empty( $page->post_title ) ) {
$attributes .= ' title="' . esc_attr( $page->post_title ) . '"';
}
$output .= '<a href="' . get_permalink( $page->ID ) . '"' . $attributes . '>';
$output .= '<i class="fa fa-file-o"></i> '; // 添加图标
$output .= apply_filters( 'the_title', $page->post_title, $page->ID );
$output .= '</a>';
}
}
在这个例子中,我们重写了 start_el
方法,在输出页面链接之前添加了一个 <i class="fa fa-file-o"></i>
标签,用于显示一个文件图标。 (注意:这里需要你已经引入了Font Awesome或其他图标库)。
要使用这个自定义 Walker
类,我们需要在 wp_list_pages
的参数中指定 walker
:
$args = array(
'depth' => 0,
'walker' => new My_Custom_Page_Walker(),
'title_li' => ''
);
wp_list_pages( $args );
Walker
类中的层级渲染逻辑
Walker
类通过维护一个内部的 $db_fields
属性来处理层级关系。 Walker_Page
类的 $db_fields
属性通常设置为:
public $db_fields = array(
'parent' => 'post_parent',
'id' => 'ID'
);
这意味着 Walker
类会使用 post_parent
字段来确定页面的父级关系,并使用 ID
字段作为页面的唯一标识符。
Walker::walk()
方法是遍历树状结构的核心。 它接收一个包含所有节点的数组,以及要显示的层级深度。 walk()
方法会递归地遍历数组,并根据 $db_fields
属性确定父子关系。
以下是 Walker::walk()
方法的简化逻辑:
public function walk( $elements, $max_depth ) {
$args = array_slice( func_get_args(), 2 );
$output = '';
$parent_field = $this->db_fields['parent'];
// first, sort it into parent posts
$children = array();
if ( ! empty( $elements ) ) {
foreach ( $elements as $e ) {
$parent = $e->$parent_field;
if ( ! isset( $children[ $parent ] ) )
$children[ $parent ] = array();
$children[ $parent ][] = $e;
}
}
$walker_args = array( $output, $elements, $max_depth, $children, $args );
$output = call_user_func_array( array( $this, 'display_element' ), $walker_args );
return $output;
}
这个方法首先将所有元素按照父级关系分组到 $children
数组中。然后,它调用 display_element()
方法来处理每个元素。
display_element()
方法负责调用 start_lvl()
、start_el()
、end_el()
和 end_lvl()
方法,并递归地处理子节点。
public function display_element( $output, &$element, $depth, $children_elements, $max_depth, ...$args ) {
if ( ! $element )
return;
$id_field = $this->db_fields['id'];
$id = $element->$id_field;
// Display this element.
if ( is_null( $max_depth ) || ( $max_depth >= $depth ) ) {
$this->has_children = ! empty( $children_elements[ $id ] );
$cb_args = array_merge( array( &$output, $element, $depth ), $args);
call_user_func_array( array( $this, 'start_el' ), $cb_args );
// Descend only when the depth is right and there are childrens for this element.
if ( ( $max_depth === false ) || ( ( $max_depth > $depth + 1 ) || ( $max_depth === null ) ) && isset( $children_elements[ $id ] ) ) {
$next_depth = $depth + 1;
$cb_args = array_merge( array( &$output, $children_elements[ $id ], $next_depth, $max_depth ), $args );
call_user_func_array( array( $this, 'display_element' ), $cb_args );
}
$cb_args = array_merge( array( &$output, $element, $depth ), $args );
call_user_func_array( array( $this, 'end_el' ), $cb_args );
}
}
这个方法首先调用 start_el()
方法来输出当前元素的起始标签和内容。然后,如果当前元素有子节点,并且当前深度小于最大深度,则递归调用 display_element()
方法来处理子节点。最后,调用 end_el()
方法来输出当前元素的结束标签。
深度控制
wp_list_pages
函数通过 depth
参数来控制显示的层级深度。 当 depth
为 0
时,表示显示所有层级。 当 depth
为一个正整数时,表示只显示指定层级的页面。
Walker::walk()
方法会根据 max_depth
参数来判断是否需要递归处理子节点。 如果当前深度大于或等于 max_depth
,则不会递归处理子节点。
总结:理解与定制化
wp_list_pages
函数是一个功能强大的函数,用于生成页面列表。通过理解其背后的递归机制和 Walker
类的工作方式,我们可以完全控制页面列表的输出,并实现各种定制化的需求。 通过重写 Walker
类的方法,我们可以添加自定义的 HTML 标签、CSS 类和图标,从而使页面列表更加符合我们的设计要求。
核心逻辑的简要概括
wp_list_pages
利用递归方式构建页面层级结构,并借助 Walker
类实现对页面列表输出的定制化控制。理解 Walker
类的核心方法和 $db_fields
属性对于修改页面列表的HTML结构至关重要。