WordPress的碎碎念/朋友圈/微博增加评论功能

多年前有人写了如何开发wordpress碎碎念链接),虽然已经10年前的代码了,但是依旧可以工作,我这边增加了行内评论显示和回复功能。让他更加像微博和朋友圈,具体的效果:https://jaketao.com/notes

具体代码分享如下:

第一步:制作碎碎念(原碎碎念制作方案,稍做改进)

1.1 新建post type

创建一个post-type.php文件放到你主题根目录(如果后台不显示,在functions里面引用下),内容是:

<?php
add_action('init', 'my_custom_init');
function my_custom_init()
{
$labels = array(
'name' => '碎语',
'singular_name' => 'Talk',
'add_new' => '发表新碎语',
'add_new_item' => '发表新碎语',
'edit_item' => '编辑碎语',
'new_item' => '新碎语',
'view_item' => '查看碎语',
'search_items' => '搜索碎语',
'not_found' => '暂无碎语',
'not_found_in_trash' => '没有已遗弃的碎语',
'parent_item_colon' => '',
'menu_name' => '碎语'
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => null,
'supports' => array('title','editor','author','comment')
);
register_post_type('talk',$args);
}
?>

1.2 写一个前端页面

复制一个page.php,命名为page-talk.php然后放在目录内,头部改成

<?php
/*
Template Name: 碎碎念
*/
?>

然后在按照你的模板修改,以下是例子,记得增加对应的css在style.css

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<?php $limit = get_option('posts_per_page');$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;query_posts('post_type=talk&post_status=publish&showposts=' . $limit=20 . '&paged=' . $paged);if (have_posts()) : while (have_posts()) : the_post(); ?>
<div class="talklist">
<span class="talkcontent">
<?php the_content(); ?>
</span>
<span>
<p class="talktime">
<?php _e('Published by','themejunkie'); ?> <?php the_author_posts_link(); ?>
<?php the_time('Y年n月j日G:i'); ?>
</p>
</span>
</div>
<?php endwhile;endif; ?>
<?php if (function_exists('wp_pagenavi')) wp_pagenavi();else { ?><div class="talkpagenavi"><?php previous_posts_link('【« 上一页】') ?> <?php next_posts_link('【下一页 »】') ?></div><?php } ?>
<?php wp_reset_query(); ?>

1.3 后台创建页面

在wordpress后台 – 页面 – 增加一个页面,使用你刚才写的模板(名字叫碎碎念),这样第一步就基本完成。

如果页面样式不符合你想要的,可以自行修改page-talk页面

第二步:增加页面评论、点赞功能

现在做的post-type其实还是一个文章,如果需要实现朋友圈、微博那样的页面看评论,需要做出以下修改:

2.1 修改functions.php

可以直接增加,或者创建一个新的functions-talk.php(记得引用)

// ==================================================
//  AJAX:拉取评论
// ==================================================
add_action('wp_ajax_load_post_comments', 'load_post_comments_ajax');
add_action('wp_ajax_nopriv_load_post_comments', 'load_post_comments_ajax');

function load_post_comments_ajax() {

$post_id = intval($_POST['post_id'] ?? 0);
if (!$post_id) {
wp_send_json_error('post_id missing');
}

$comments = get_comments([
'post_id' => $post_id,
'status' => 'approve',
'orderby' => 'comment_date',
'order' => 'ASC',
]);

$data = [];

foreach ($comments as $c) {
$data[] = [
'id' => $c->comment_ID,
'parent' => $c->comment_parent,
'author' => $c->comment_author,
'content' => wpautop($c->comment_content),
'date' => $c->comment_date,
'post_id' => $c->comment_post_ID,
'user_id' => $c->user_id ?? 0,
'avatar' => get_avatar_url($c->user_id ?? 0, ['size' => 60]),
'user_url' => $c->comment_author_url ?: '',
'parent_author' => $c->comment_parent
? get_comment($c->comment_parent)->comment_author
: '',
'parent_user_id' => $c->comment_parent
? get_comment($c->comment_parent)->user_id
: 0,
'parent_url' => $c->comment_parent
? get_comment_link($c->comment_parent)
: '',
'children' => [],
];
}

// 构建嵌套评论树
$nest_comments = function (array $comments) {
$tree = [];
$map = [];

foreach ($comments as &$c) {
$map[$c['id']] = &$c;
$c['children'] = [];
}

foreach ($comments as &$c) {
if ($c['parent'] && isset($map[$c['parent']])) {
$map[$c['parent']]['children'][] = &$c;
} else {
$tree[] = &$c;
}
}

return $tree;
};

wp_send_json_success($nest_comments($data));
}


// ==================================================
// AJAX:加载评论表单
// ==================================================
add_action('wp_ajax_load_comment_form', 'load_comment_form_ajax');
add_action('wp_ajax_nopriv_load_comment_form', 'load_comment_form_ajax');

function load_comment_form_ajax() {

$post_id = intval($_POST['post_id'] ?? 0);
if (!$post_id) {
wp_send_json_error();
}

ob_start();

comment_form([
'title_reply' => '发表回复',
'comment_notes_before' => '',
'comment_notes_after' => '',
'logged_in_as' => '',
'label_submit' => '提交',
'id_form' => 'commentform',
'class_form' => 'comment-form',
'comment_field' => '
<div class="comment-form-comment">
<textarea
id="comment"
name="comment"
class="required"
rows="4"
placeholder="写下你的评论…"
required
></textarea>

<div class="comment-form-smile j-smilies" data-target="#comment">
<i class="wpcom-icon wi smile-icon">
<svg aria-hidden="true">
<use xlink:href="#wi-emotion"></use>
</svg>
</i>
</div>
</div>

',
], $post_id);

$form_html = ob_get_clean();

// 移除最外层 #respond(如存在)
$form_html = preg_replace(
'/<div id="respond" class="comment-respond">|<\/div>$/',
'',
$form_html
);

wp_send_json_success($form_html);
}


// ==================================================
// AJAX:提交评论
// ==================================================
add_action('wp_ajax_submit_comment', 'submit_comment_ajax');
add_action('wp_ajax_nopriv_submit_comment', 'submit_comment_ajax');

function submit_comment_ajax() {

if (!is_user_logged_in()) {
wp_send_json_error('need_login');
}

$user = wp_get_current_user();

$commentdata = [
'comment_post_ID' => intval($_POST['post_id']),
'comment_content' => trim($_POST['content']),
'comment_parent' => intval($_POST['parent']),
'user_id' => $user->ID,
'comment_author' => $user->display_name,
'comment_author_email' => $user->user_email,
'comment_author_url' => $user->user_url,
];

$comment_id = wp_new_comment($commentdata);

if (!$comment_id) {
wp_send_json_error('insert_failed');
}

wp_send_json_success([
'comment_id' => $comment_id,
]);
}


// ==================================================
// 前端脚本注册(仅碎碎念列表页)
// ==================================================
add_action('wp_enqueue_scripts', 'enqueue_post_comments_js');

function enqueue_post_comments_js() {

if (!is_page(9890)) {
return;
}

wp_enqueue_script(
'post-comments',
get_stylesheet_directory_uri() . '/js/post-comments.js',
['jquery'],
null,
true
);

wp_localize_script(
'post-comments',
'ajaxurl',
admin_url('admin-ajax.php')
);
}


// ==================================================
// 自定义提交按钮外观(仅碎碎念页)
// ==================================================
add_filter('comment_form_submit_field', function ($submit_field) {

if (!is_page(9890)) {
return $submit_field;
}

preg_match_all('/<input type="hidden".*?>/i', $submit_field, $matches);
$hidden = implode("\n", $matches[0]);

if (is_user_logged_in()) {
$user = wp_get_current_user();
$avatar = get_avatar($user->ID, 60);
$name = esc_html($user->display_name);
} else {
$avatar = '';
$name = '';
}

return '
<div class="form-submit">
<div class="pull-left form-submit-text">
' . $avatar . '
<span>' . $name . '</span>
</div>

<button
name="submit"
type="submit"
id="submit"
class="wpcom-btn btn-primary btn-xs submit"
>
提交
</button>

' . $hidden . '
</div>';
}, 20);

 

这里直接用AJAX重新弄了一个评论区域并且强制需要登陆才能回贴,如果想直接使用原生态的,可以自行修改。另外我是直接用了wpcom的css,如果需要自行修改可以改下class和css文件

2.2 增加post-comments.js文件

我的路径是js/post-commnents.js,需要修改自行修改

jQuery(function ($) {




  /* ==================================================

   * 展开 / 加载评论

   * ================================================== */

  $('.toggle-comments').on('click', function () {




    const postId = $(this).data('post-id');

    const box    = $('#comment-box-' + postId);

    const list   = box.find('.comments-list');




    // 已加载过:仅切换显示

    if (box.data('loaded')) {

      box.toggle();

      return;

    }




    $.post(ajaxurl, {

      action:  'load_post_comments',

      post_id: postId

    }, function (res) {




      if (!res.success) return;




      // 最新评论在前

      const sortedComments = res.data.sort(

        (a, b) => new Date(b.date) - new Date(a.date)

      );




      list.html(renderCommentsNested(sortedComments));

      box.show().data('loaded', true);




      // 更新评论数量

      box.find('.comments-title')

        .text(`评论列表(${res.data.length}条)`);




      // 仅加载一次评论表单

      loadCommentForm(postId, box);

    });

  });







  /* ==================================================

   * AJAX 加载评论表单

   * ================================================== */

  function loadCommentForm(postId, box) {




    const respond = box.find('#respond');

    if (respond.data('loaded')) return;




    $.post(ajaxurl, {

      action:  'load_comment_form',

      post_id: postId

    }, function (res) {




      if (!res.success) return;




      respond.html(res.data);




      const form = respond.find('#commentform');

      if (!form.length) return;




      // comment_post_ID

      if (!form.find('input[name="comment_post_ID"]').length) {

        form.append(

          `<input type="hidden" name="comment_post_ID" value="${postId}">`

        );

      } else {

        form.find('input[name="comment_post_ID"]').val(postId);

      }




      // comment_parent

      if (!form.find('input[name="comment_parent"]').length) {

        form.append(

          `<input type="hidden" name="comment_parent" value="0">`

        );

      } else {

        form.find('input[name="comment_parent"]').val(0);

      }




      respond.data('loaded', true);

    });

  }







  /* ==================================================

   * 渲染嵌套评论(不滚动)

   * ================================================== */

  function renderCommentsNested(comments, parentId = 0, depth = 1) {




    let html = '';




    comments

      .filter(c => c.parent == parentId)

      .forEach((c, index) => {




        const isEven  = index % 2 === 0;

        const classes = [

          'comment',

          depth > 1 ? '' : (isEven ? 'even' : 'odd'),

          c.children && c.children.length ? 'parent' : '',

          'depth-' + depth

        ].join(' ').trim();




        html += `

<li class="${classes}" id="comment-${c.id}">

  <div id="div-comment-${c.id}" class="comment-inner">




    <div class="comment-author vcard">

      <img

        alt="${c.author}的头像"

        src="${c.avatar}"

        class="avatar avatar-60 photo"

        height="60"

        width="60"

      >

    </div>




    <div class="comment-body">

      <div class="nickname">

        ${

          c.user_url

            ? `<a href="${c.user_url}" class="url j-user-card" data-user="${c.user_id}" target="_blank" rel="ugc">${c.author}</a>`

            : c.author

        }

        <span class="comment-time">${c.date}</span>

      </div>




      <div class="comment-text">

        ${

          c.parent_author

            ? `<span class="comment-text-reply">

                 <a class="j-user-card" data-user="${c.parent_user_id}" href="${c.parent_url}" target="_blank">

                   @${c.parent_author}

                 </a>:

               </span>`

            : ''

        }

        ${c.content}

      </div>

    </div>




    <div class="reply">

      <a

        rel="nofollow"

        class="comment-reply-link"

        href="javascript:;"

        data-commentid="${c.id}"

        data-postid="${c.post_id}"

        data-belowelement="div-comment-${c.id}"

        data-respondelement="respond"

        data-replyto="回复给 ${c.author}"

        aria-label="回复给 ${c.author}"

      >

        <i class="wpcom-icon wi">

          <svg aria-hidden="true">

            <use xlink:href="#wi-comment-fill"></use>

          </svg>

        </i>

        <span>回复</span>

      </a>

    </div>




  </div>




  ${

    c.children && c.children.length

      ? `<ul class="comment-children">

           ${renderCommentsNested(c.children, c.id, depth + 1)}

         </ul>`

      : ''

  }

</li>`;

      });




    return html;

  }







  /* ==================================================

   * 提交评论(AJAX)

   * ================================================== */

  $(document).on('submit', '#commentform', function (e) {




    e.preventDefault();




    const form    = $(this);

    const postId  = form.find('input[name="comment_post_ID"]').val();

    const parent  = form.find('input[name="comment_parent"]').val() || 0;

    const content = form.find('#comment').val().trim();




    if (!content) return;




const subscribe = form.find('#cren_subscribe_to_comment').is(':checked') ? 'on' : '';

    $.post(ajaxurl, {

      action:  'submit_comment',

      post_id: postId,

      parent:  parent,

      content: content,

      author:  form.find('#author').val(),

      email:   form.find('#email').val(),

  cren_subscribe_to_comment: subscribe

    }, function (res) {




      if (!res.success) {

        if (res === 'need_login') {

          alert('请先登录后评论');

          location.href = '/wp-login.php';

        }

        return;

      }




      // 重置表单

      form[0].reset();

      form.find('input[name="comment_parent"]').val(0);




      // 重新加载评论

      $.post(ajaxurl, {

        action:  'load_post_comments',

        post_id: postId

      }, function (res2) {




        if (!res2.success) return;




        const sortedComments = res2.data.sort(

          (a, b) => new Date(b.date) - new Date(a.date)

        );




        const box = $('#comment-box-' + postId);




        box.find('.comments-list')

          .html(renderCommentsNested(sortedComments));




        box.find('.comments-title')

          .text(`评论列表(${res2.data.length}条)`);

      });




      // 取消回复,回到顶部

      $('#cancel-comment-reply-link').trigger('click');

    });

  });







  /* ==================================================

   * 点击“回复”

   * ================================================== */

  $(document).on('click', '.comment-reply-link', function (e) {




    e.preventDefault();




    const parentId = $(this).data('commentid');

    const box      = $(this).closest('.entry-comments');

    const respond  = box.find('#respond');

    const target   = $('#div-comment-' + parentId);




    // 移动表单到目标评论下

    respond.appendTo(target);

    respond.find('input[name="comment_parent"]').val(parentId);

    respond.find('#comment').focus();




    $('#cancel-comment-reply-link').show();

  });







  /* ==================================================

   * 取消回复(回到顶部)

   * ================================================== */

  $(document).on('click', '#cancel-comment-reply-link', function (e) {




    e.preventDefault();




    const respond = $('#respond');

    const box     = respond.closest('.entry-comments');




    respond.find('input[name="comment_parent"]').val(0);

    box.prepend(respond);




    $(this).hide();

  });




});

 

2.3  自动给碎语增加标题(可选)

因为碎语只有文章内容本体,标题都会是未命名,不是很雅观,这里代码直接取内容本体作为标题。

add_action('save_post_talk', 'talk_generate_title_after_save', 20, 3);
function talk_generate_title_after_save($post_id, $post, $update) {

// 1. 排除自动保存 / 修订版本
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
return;
}

// 2. 只在 publish / future / draft 时处理(避免 auto-draft 干扰)
if (!in_array($post->post_status, ['publish', 'future', 'draft'], true)) {
return;
}

// 3. 如果已经有有效标题,则不处理
if (!empty($post->post_title) && $post->post_title !== '自动草稿') {
return;
}

// 4. 取内容生成标题
$content = trim(wp_strip_all_tags($post->post_content));

if (!empty($content)) {
$new_title = wp_trim_words($content, 10, '…');
} else {
$new_title = '碎语';
}

// 5. 防止递归触发
remove_action('save_post_talk', 'talk_generate_title_after_save', 20);

wp_update_post([
'ID' => $post_id,
'post_title' => $new_title,
]);

add_action('save_post_talk', 'talk_generate_title_after_save', 20, 3);
}

这样就大功告成,但是应该会有一些问题,前端格式问题直接改css和classname。效果如下(测试:https://jaketao.com/notes

WX20251223 153402 Jake blog WX20251223 153343 Jake blog

本站原创文章皆遵循“署名-非商业性使用-相同方式共享 3.0 (CC BY-NC-SA 3.0)”。转载请保留以下标注:

原文来源:《WordPress的碎碎念/朋友圈/微博增加评论功能》

2
0 0 2

延伸阅读

发表回复

登录后才能评论
分享本页
返回顶部