原始方案是页面每次请求,阅读次数在后台就+1了,这样出现一个问题:如果是爬虫抓取,或者简单的命令行加载某个 html 页面,阅读数也会增加;
这种情况下,肯定不能让阅读次数增加,抓取页面的时候,虽然是返回了 HTML,但是爬虫并不会将页面内容展示出来,只是将返回页面当做字符串处理,截取需要的内容,这时候页面 JavaScript 是不会执行的;
利用爬虫抓取页面 JS 不执行的特点,修改为页面加载完成后,发出 ajax 请求增加阅读次数,这样就过滤掉了爬虫,于是就有了下面的最新方案(2019-01-29 更新):
0、公共方法
公共方法是 WordPress 新增功能几篇文章中代码的基础,在这儿添加一下,其他文章中不再重复;
// 获取 int 类型的 meta 标签
function get_int_meta($key)
{
global $post;
$post_ID = $post->ID;
$count = (int)get_post_meta($post_ID, $key, true);
return $count;
}
/// 判断当前用户是否为管理员
function user_is_admin()
{
// wp_get_current_user函数仅限在主题的functions.php中使用
$currentUser = wp_get_current_user();
return !empty($currentUser->roles) && in_array('administrator', $currentUser->roles);
}
// ajax 失败公共函数
function ajax_error($msg = '操作失败,请稍后再试')
{
header('Content-Type: application/json,charset=utf-8');
echo json_encode(array(code => 0, msg => $msg));
wp_die();
}
// ajax 返回公共调试信息
function ajax_debug()
{
$method = $_SERVER['REQUEST_METHOD'];
$info = array(
method => $method,
debug => ($host != 'ashita.top' && $host != 'www.ashita.top')
);
return $info;
}
// ajax 成功公共回调
function ajax_success($res, $msg = '操作成功')
{
header('Content-Type: application/json,charset=utf-8');
$resp = array(code => 1, msg => $msg, data => $res);
$debug = ajax_debug();
$debug['debug'] && ($resp['debug'] = $debug);
echo json_encode($resp);
wp_die();
}
1、记录访问次数
主题根目录下的 functions.php 中,在最后添加如下代码:
define('my_views', 'my_views');
/// 记录文章的阅读次数
function record_visitors()
{
//delete_post_meta_by_key('views');
$method = $_SERVER['REQUEST_METHOD']; // 获取请求 method
$post_id = $_POST['id']; // 获取文章id
$ua = get_user_agent(); // 获取ua
// 只有method为post、文章id有值、ua有值、非管理员、数据库中能查到该记录
if ($method !== 'POST' || !$post_id || !$ua || user_is_admin() || !get_post($post_id))
return ajax_error();
$post_views = (int)get_post_meta($post_id, my_views, true);
if (!update_post_meta($post_id, my_views, ($post_views + 1)))
add_post_meta($post_id, my_views, 1, true);
$res = array(count => $post_views + 1, id => $post_id);
ajax_success($res);
}
function get_meta_views()
{
return get_int_meta(my_views);
}
add_action('wp_ajax_visitor', 'record_visitors');
add_action('wp_ajax_nopriv_visitor', 'record_visitors');
// add_action('wp_head', 'record_visitors');
2、添加前端 ajax 请求
主题根目录下的 single.php 中,页面底部 “<?phpget_footer(); ” 之前,新增下面的代码:
<script>
(function () {
var $ = jQuery;
// ajax 请求地址和文章id
var url = "<?php echo admin_url('admin-ajax.php')?>";
var id = '<?php the_ID(); ?>';
$(function () {
highCode();
vote();
freshVisitor();
});
// 代码高亮
function highCode() {}
// 投票
function vote() {}
// 更新阅读次数
function freshVisitor() {
$.ajax({
url: url,
type: 'post',
data: {
action: 'visitor',
id: id, // post_id
},
success: function (resp) {
//console.log(resp);
if (resp.code !== 1) return console.log('操作失败,请稍后再试');
var count = resp.data.count;
var $span = $('.entry-meta').find('.fa.fa-eye');
$span.text($span.text().replace(/^\d+/, count));
},
error: function () {
//console.log('操作失败,请稍后再试');
},
complete: function () {
}
});
}
})();
</script>
投票和代码高亮的功能请到“运维管理”分类下查找
*3、调用访问次数
这个调用只是为了页面初始化加载进来的时候,默认显示最新的阅读次数(不包括当前访问次数),不加也可以,因为 ajax 请求成功后更新了这个值;
在 inc/template-tags.php 中,直接 get_meta_views() 就能获取访问次数了,每个主题下该文件内容不一样,看情况加入下面的代码:
echo '<span class="fa-eye fa">' . get_meta_views() . '次浏览</span>';
上面的代码表示输出一个 span 标签,其中,fa fa-eye 这两个 class 也是根据主题来的,我用的 blog kit 主题有自己的字体图标,直接用就行了;
此处直接用 get_int_meta(my_views) 替换 get_meta_views() 也是可以的,但考虑到变量分散到多个地方,不好管理,所以将变量的声明、使用都统一放到了 functions.php 中;
下面是最初的原始方案,只是记录一下,没什么用了;
1、记录访问次数
打开主题根目录下的 functions.php 文件,在最后添加下面的代码:
define('my_views', 'my_views');
/// 记录文章的阅读次数
function record_visitors()
{
// 非管理员且是单页面,添加/更新 my_views 字段
if (!user_is_admin() && is_singular()) {
global $post;
$post_ID = $post->ID;
delete_post_meta_by_key('views');
if ($post_ID) {
$post_views = (int)get_post_meta($post_ID, my_views, true);
if (!update_post_meta($post_ID, my_views, ($post_views + 1))) {
add_post_meta($post_ID, my_views, 1, true);
}
}
}
}
add_action('wp_head', 'record_visitors');
// 获取文章的访问次数
function get_meta_views()
{
return get_int_meta(my_views);
}
// 获取 int 类型的 meta 标签值
function get_int_meta($key)
{
global $post;
$post_ID = $post->ID;
$count = (int)get_post_meta($post_ID, $key, true);
return $count;
}
// 判断当前用户是否为管理员
function user_is_admin()
{
// wp_get_current_user函数仅限在主题的functions.php中使用
$currentUser = wp_get_current_user();
return !empty($currentUser->roles) && in_array('administrator', $currentUser->roles);
}
- record_visitors 中,有 post_meta 相关的操作,用我自己的话说,post_meta 表示页面的一个附属标签库;WrodPress 中 post 基本就表示页面,meta 表示标签,官方定义及术语可参考 这里;
- 标签库可以往里边随便加标签,标签包含 key 和 value,key 不能重复;数据库对应 xx_postmeta,里边有四列,meta_id、post_id、meta_key、meta_value;
- record_visitors 方法是从 这里 找到的,拿来就能直接用;
- user_is_admin 方法的内容是从 这里 找到的;
- 统计访问次数,需要把管理员登录后的访问过滤掉,这样数据才准确;
- get_meta_views 和 get_int_meta 两个方法分开,是因为下一篇文章也需要用到 get_int_meta 方法;
- 因为 PHP 没有枚举类型,所以将 my_views 定义为一个常量,其他用到的地方都直接引用这个常量;
2、调用访问次数
在 inc/template-tags.php 中,直接 get_meta_views() 就能获取访问次数了,每个主题下该文件内容不一样,看情况加入下面的代码:
echo '<span class="fa-eye fa">' . get_meta_views() . '次浏览</span>';
上面的代码表示输出一个 span 标签,其中,fa fa-eye 这两个 class 也是根据主题来的,我当前用的 blog kit 主题有自己的字体图标,直接用就行了;
此处直接用 get_int_meta(my_views) 替换 get_meta_views() 也是可以的,但考虑到变量分散到多个地方,不好管理,所以将变量的声明、使用都统一放到了 functions.php 中;