文章导航是什么? 一个外国朋友告诉我, 他们管它叫 Breadcrumb trail 或者 Breadcrumb navigation, 翻译过来就是文章导航或者文章路径, 对吧? 具体叫什么我真不确定, 或者看看下面的截图你就会知道我在说什么了.
这个友好的导航功能, 我最先是在论坛上看到的, 形式一般如下:
首页 > 模块 > 文章
有了它, 访客就可以方便地访问同类的文章, 并明确文章的所属, 对提高用户体验和网站的 PV 值都很有帮助. 我在制作 iNove 主题的时候也加上了这个功能, 但是只能处理子分类, 形式如下:
首页 > 分类 > 文章
最近我在制作一个主题, 客户要求能实现多层次的文章导航, 形式如下:
首页 > 分类 > 子分类 > 第二层子分类 > ... > 第 N 层子分类 > 文章
一般我们会在 the_post()
后面调用 the_category()
生成分类内容. 为此我翻了一下 WordPress 的源代码, 发现 the_category()
方法可能会用到 category-template.php 文件里的另两个方法. 下面我将以注释的形式讲解一下这两个方法, 如发现误解之处, 请指正.
获取父级分类 get_category_parents
/**
* 获取父级分类 (包含分隔符).
*
* @since 1.2.0
*
* @param int $id 分类 ID.
* @param bool $link 默认为 false, 为真时返回的分类包含链接.
* @param string $separator 分类间分隔符, 默认是 '/'.
* @param bool $nicename 默认为 false, 为真时返回分类显示为分类别名, 否则显示分类名.
* @param array $visited 防止分类重复显示 (调用该方法时请不用理会这个参数, 本人认为这只是为了顺利完成迭代处理).
* @return string
*/
function get_category_parents( $id, $link = false, $separator = '/', $nicename = false, $visited = array() ) {
// 准备返回的 HTML 字符串
$chain = '';
// 通过 ID 获取分类
$parent = &get_category( $id );
// 如果该分类已经定义过, 立即返回
if ( is_wp_error( $parent ) )
return $parent;
// 以分类别名或者分类名作为分类链接的显示文本
if ( $nicename )
$name = $parent->slug;
else
$name = $parent->cat_name;
// 如果该分类还有父分类, 分类不是连向自己, 并且还没有处理过
if ( $parent->parent && ( $parent->parent != $parent->term_id ) && !in_array( $parent->parent, $visited ) ) {
// 把当前分类的父分类添加到已处理的数组中, 避免以后被重复处理
$visited[] = $parent->parent;
// 迭代处理父分类
$chain .= get_category_parents( $parent->parent, $link, $separator, $nicename, $visited );
}
// 以链接或者文本形式 (包含分隔符) 将分类拼接到准备返回的 HTML 字符串中
if ( $link )
$chain .= '<a href="' . get_category_link( $parent->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $parent->cat_name ) . '">'.$name.'</a>' . $separator;
else
$chain .= $name.$separator;
// 返回 HTML 字符串 (结果)
return $chain;
}
获取父级分类 get_the_category_list
/**
* 获取分类列表, 并以 HTML 列表方式或者自定义格式返回.
*
* @since 1.5.1
*
* @param string $separator 默认为空, 显示在各分类之间的分隔符.
* @param string $parents 指示如何显示父级的分类.
* @param int $post_id 获取某一文章 ID 对应的分类.
* @return string
*/
function get_the_category_list( $separator = '', $parents='', $post_id = false ) {
global $wp_rewrite;
// 找到当前文章对应的分类 (数组, 因为文章可以属于多个分类)
$categories = get_the_category( $post_id );
// 如果分类数组为空, 在 the_category 方法中当作 '未分类' 进行处理
if ( empty( $categories ) )
return apply_filters( 'the_category', __( 'Uncategorized' ), $separator, $parents );
$rel = ( is_object( $wp_rewrite ) && $wp_rewrite->using_permalinks() ) ? 'rel="category tag"' : 'rel="category"';
// 准备返回的 HTML 字符串
$thelist = '';
// 如果分隔符为空, 返回分类的 HTML 列表
if ( '' == $separator ) {
// 列表的开头, 是一个 unordered list
$thelist .= '<ul class="post-categories">';
// 循环处理所有分类
foreach ( $categories as $category ) {
// 每个分类项的开头
$thelist .= "\n\t<li>";
switch ( strtolower( $parents ) ) {
// 如果以 'multiple' 模式显示父分类, 每层的分类会独立成一个链接
case 'multiple':
// 父分类存在的话, 获取父分类的 HTML 代码并拼接到准备输出的 HTML 字符串中
if ( $category->parent )
$thelist .= get_category_parents( $category->parent, true, $separator );
// 最后把当前分类的 HTML 代码和分类也拼接到准备输出的 HTML 字符串中
$thelist .= '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . $rel . '>' . $category->name.'</a></li>';
break;
// 如果以 'single' 模式显示父分类, 所有分类作为一个链接
case 'single':
// 链接的开头
$thelist .= '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . $rel . '>';
// 父分类存在的话, 获取父分类的 HTML 代码并拼接到准备输出的 HTML 字符串中
if ( $category->parent )
$thelist .= get_category_parents( $category->parent, false, $separator );
// 当然分类的名字和链接的结尾
$thelist .= $category->name.'</a></li>';
break;
// 如果以默认模式显示父分类, 即不显示父分类
case '':
default:
$thelist .= '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . $rel . '>' . $category->cat_name.'</a></li>';
}
}
// 列表的结尾
$thelist .= '</ul>';
// 如果分隔符不为空, 返回一段格式化的 HTML
} else {
// 计数器
$i = 0;
// 循环处理所有分类
foreach ( $categories as $category ) {
// 第一个分类的前面不显示分隔符
if ( 0 < $i )
$thelist .= $separator . ' ';
switch ( strtolower( $parents ) ) {
// 如果以 'multiple' 模式显示父分类, 每层的分类会独立成一个链接
case 'multiple':
if ( $category->parent )
$thelist .= get_category_parents( $category->parent, true, $separator );
$thelist .= '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . $rel . '>' . $category->cat_name.'</a>';
break;
// 如果以 'single' 模式显示父分类, 所有分类作为一个链接
case 'single':
$thelist .= '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . $rel . '>';
if ( $category->parent )
$thelist .= get_category_parents( $category->parent, false, $separator );
$thelist .= "$category->cat_name</a>";
break;
case '':
// 如果以默认模式显示父分类, 即不显示父分类
default:
$thelist .= '<a href="' . get_category_link( $category->term_id ) . '" title="' . sprintf( __( "View all posts in %s" ), $category->name ) . '" ' . $rel . '>' . $category->name.'</a>';
}
// 计数器自增
++$i;
}
}
return apply_filters( 'the_category', $thelist, $separator, $parents );
}
在主题中加入文章导航
1. 不包含父级分类的文章导航
这里用到 the_category()
方法的默认模式, 会调用 get_the_category_list()
生成最后一层的子分类 (分类可能不只一个) 组成的 HTML 字符串并打印到页面上. 代码如下:
<a title="Go to homepage" href="<?php echo get_settings('home'); ?>/">Home</a> <!-- 首页链接 -->
> <?php the_category(', '); ?> <!-- 每个分类以逗号分隔 -->
> <?php the_title(); ?> <!-- 文章链接 -->
2. 包含父级分类的文章导航
我们要用的是 the_category()
方法的 multiple 模式, 而他会调用 get_the_category_list()
和 get_category_parents()
两个方法生成一个包含所有父分类的 HTML 字符串并打印到页面上. 代码如下:
<a title="Go to homepage" href="<?php echo get_settings('home'); ?>/">Home</a> <!-- 首页链接 -->
> <?php the_category(' > ', 'multiple'); ?> <!-- 每个分类以 > 分隔, 父级分类在前, 子分类在后 -->
> <?php the_title(); ?> <!-- 文章链接 -->
当然, 只要你愿意, 可以将这段代码取代 iNove 主题里 single.php 文件的 id="postpath"
部分, 我并不准备在以后版本的 iNove 主题中使用这个多级的文章导航.