WordPress 中添加 ajax 请求

上篇 WordPress 文章中添加了文章阅读次数;本篇添加一个点赞的功能;样式是下图这样,或者直接拉到文章底部进行体验;

该功能可以在一定程度上得到访客的反馈,所以试着加一下该功能;原理是利用上篇中提到的 meta 标签/元数据 进行数据的存储;该功能涉及的逻辑如下:

  • 访客觉得内容有用,可以点个赞,点赞区域显示已点赞状态;
  • 如果误操作,可以再点一次进行取消,点赞区域恢复默认状态;
  • 用户对文章的点赞行为存储在 localStorage/cookie 中;
  • 如果未清除 localStorage/cookie,每篇文章最多点赞一次;
  • 如果清除了值或者换了浏览器,可以再次进行点赞;
  • wp 本身就没有针对访客的登录系统,想要完全真实有效的数据不可能,所以点赞数据仅供参考;
  • 最初设计的是两个按钮,顶和踩,后来发现逻辑太过复杂,需要判断当前点的是顶还是踩、当前点击本身的状态、另一个按钮的状态(两个按钮互斥)、之前是否顶或踩过,变量太多,加这么一个本来就不是很准确的功能,太浪费资源;

下面是修改的步骤:

1、添加后端操作

打开主题根目录下的 functions.php,在最后添加如下代码:

define('my_up', 'my_up');
function get_meta_up()
{
    return get_int_meta(my_up);
}
// 获取 int 类型的 meta 标签
function get_int_meta($key)
{
    global $post;
    $post_ID = $post->ID;
    $count = (int)get_post_meta($post_ID, $key, true);
    return $count;
}
// ajax 请求
function ajax_useful()
{
    $key = my_up;
    $type = $_POST['type'];
    $post_id = $_POST['id'];
    // 当前请求为get方式或当前的post_id没有对应文章,直接结束
    if ($_SERVER['REQUEST_METHOD'] == 'GET' || !get_post($post_id)) return wp_die();

    $dir = $type == '1' ? 1 : -1;
    $post_count = (int)get_post_meta($post_id, $key, true) + $dir;

    if (!update_post_meta($post_id, $key, $post_count)) {
        $post_count = 1;
        add_post_meta($post_id, $key, $post_count, true);
    }
    $res = array(code => 1, realCount => $post_count);
    header('Content-Type: application/json,charset=utf-8');
    echo json_encode($res);
    wp_die();
}
add_action('wp_ajax_useful', 'ajax_useful');
add_action('wp_ajax_nopriv_useful', 'ajax_useful');
  • 其中 get_int_meta 方法和 meta 的操作在 上篇 中有提到,这里就不再赘述;
  • $_SERVER[‘REQUEST_METHOD’] – 获取请求方法;
  • $_POST[‘id’] – 获取请求参数值;
  • header(”) – 直接设置响应头;
  • json_encode() – 将响应数据格式化为 json 数据;
  • wp_ajax_自定义action – ajax 钩子,按照命名规则就能捕获 ajax 发出的请求;wp 中 ajax 的请求 url 是相同的,需要通过 action 来区分是走哪个分支,所以前端 ajax 请求的时候需要在 data 中显式声明 action 这个属性;wp_ajax_ 前缀表示已登录用户走的 action/分支;
  • wp_ajax_nopriv_自定义action – 同样的 ajax 钩子,这个前缀表示未登录用户走的 action/分支,系统通过前缀做区分,可以控制两种用户走不同的分支,这里我是走的同一个分支;
  • wp_die() – 不知道为什么调用这个,详情参考 这里

2、添加页面结构

我把点赞模块放在了文章内容之后,底部导航(上一篇/下一篇)之前;

打开主题根目录下的 single.php,
在 the_post_nagivation() 之前,添加 html 代码(中间的 echo 为新增):

get_template_part('template-parts/content', get_post_type());

echo '<div class="useful">
    <h6>如果这篇文章对你有用,可以点击下面的按钮告诉我</h6>
    <i class="fa fa-thumbs-o-up" title="对我有用"></i>
    <p>' . get_meta_up() . '</p>
</div>';

the_post_navigation(……

我用的主题中包含 iconfont,其中 fa-thumbs-o-up 为空心的小手,fa-thumbs-up 为实心小手,分别对应未点赞和已点赞两个状态;

底部 script 标签中新增 js 代码:

(function () {
    var $ = jQuery;
    $(function () {
        //highCode();
        vote();
    });
    // 投票
    function vote() {
        var cookie = {
            set: function (name, value, expiredays) {
                var exdate = new Date();
                expiredays = expiredays || 0;
                exdate.setDate(exdate.getDate() + expiredays);
                document.cookie = name + "=" + encodeURI(value) + ";expires=" + exdate.toGMTString();
            },
            get: function (name) {
                var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
                if (arr = document.cookie.match(reg)) return decodeURIComponent(arr[2]);
                else return null;
            }
        };

        var url = "<?php echo admin_url('admin-ajax.php')?>";
        var id = '<?php the_ID(); ?>';
        var cls = 'fa-thumbs-up';
        var key = 'meta_' + id;
        var isRequest = false; // 是否在请求中
        var $box = $('.useful');

        // 投过票
        var value = String(getKey(key));
        if (value === '1') $box.find('i').addClass(cls);

        $box.on('click', 'i', function () {
            var $item = $(this);
            var $par = $item.parent();
            var sel = $item.hasClass(cls) ? 0 : 1;// 当前是选中的,点击取消
            var $p = $par.find('p');
            if (isRequest) return;
            isRequest = true;

            $.ajax({
                url: url,
                type: 'post',
                data: {
                    action: 'useful',
                    id: id, // post_id
                    type: sel // 选中还是取消 0-取消
                },
                success: function (resp) {
                    console.log(resp);
                    if (resp.code !== 1) return alert('操作失败,请稍后再试');

                    var arr = sel === 0 ? ['removeClass', -1] : ['addClass', +1];
                    $p.text(Number($p.text()) + arr[1]); // 点赞数量最好前端处理,因为后端返回值可能不是±1
                    setKey(key, sel);
                    $item[arr[0]](cls);
                },
                error: function () {
                    alert('操作失败,请稍后再试');
                },
                complete: function () {
                    isRequest = false;
                }
            });
        });
        function setKey(key, value) {
            if (window.localStorage) localStorage.setItem(key, value);
            else cookie.set(key, value, 30);
        }
        function getKey(key) {
            if (window.localStorage) return localStorage.getItem(key);
            return cookie.get(key);
        }
    }
})();

3、添加 CSS 样式

打开根目录下 style.css,在最后添加下面的 css,这部分也可以忽略,样式根据主题的不同有所区别:

/* 点赞 */
.useful {
  margin: 40px auto 20px;
  text-align: center;
}
.useful h6 {
  font-weight: normal;
  font-size: 14px;
  padding-bottom: 10px;
}
.useful p {
  font-size: 18px;
  color: #666;
}
.useful i {
  width: 50px;
  height: 50px;
  text-align: center;
  line-height: 50px;
  border-radius: 100px;
  font-size: 22px;
  margin-bottom: 4px;
  cursor: pointer;
  color: #5fba7d;
  border: 1px solid #5fba7d;
}
.useful p {
  color: #5fba7d;
  -webkit-user-select:none;
  -moz-user-select:none;
  -o-user-select:none;
  user-select:none;
}
.useful i:hover {
  color: #0BBA2F;
  border-color: #0BBA2F;
}


现在已经在 wp 中加了三个功能,今天登录后台提示主题有新版本,突然发现,如果更新主题所有的改动就没了… 查了下文档,得用子主题解决这个问题;
子主题 style.css页面模板是直接覆盖父主题对应文件的;如果我更新了主题(大概率是样式/页面模板的变动),然后又用子主题模板覆盖新的主题模板,并没有用到新主题的功能,我还更新干啥… 所以目前还不是问题,不用解决;

顺便说一下,我本地跑的 wp 代码放在了git上,每个功能、样式的变动都有对应的commit,即使线上出了问题,也可以直接覆盖过去;

如果这篇文章对你有用,可以点击下面的按钮告诉我

0

发表回复