You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于AJAX与Symfony的点赞计数器无刷新更新问题求助

问题描述

网站点赞系统已搭建,点击时点赞按钮可在空心/实心状态间切换,后端功能运行正常,但点赞计数器无法实现无刷新更新,作为AJAX新手尝试网上示例未解决,附上代码求助。

代码展示

CommentLikeController.php

/**
     * @Route("/comments/like-unlike", name="app_comment_like_unlike")
     */
    public function like(Request $request, EntityManagerInterface $entityManager): Response
    {

        if ($request->getMethod() === 'POST'){

            //retrieve commentId from AJAX request
            $commentId = $request->request->get('entityId');

            //check if comment exists
            $comment = $entityManager->getRepository(Comment::class)->findOneBy(['id'=> $commentId]);

            // return void if not
            if (!$comment){
                return new JsonResponse();
            }

            // retrieve csrf Token from AJAX request
            $submittedToken = $request->request->get('csrfToken');

            // check if it's valid
            if ($this->isCsrfTokenValid('comment' . $comment->getId(), $submittedToken)){

                $user = $this->getUser();

                $commentAlreadyLiked = $entityManager->getRepository(CommentLike::class)
                    ->findOneBy(['user' => $user, 'comment' => $comment]);

                // if user unlikes the comment, remove it from DB
                if($commentAlreadyLiked){
                    $entityManager->remove($commentAlreadyLiked);
                    $entityManager->flush();
                    return new JsonResponse();
                    // if user likes the comment, add record to DB
                } else{
                    $like = new CommentLike();
                    $like->setUser($user);
                    $like->setComment($comment);
                    $entityManager->persist($like);
                    $entityManager->flush();
                }

            }

        }
        return new JsonResponse();
    }

index.html.twig

<div class="js-likes-unlikes">
    <a href="{{ path('app_comment_like_unlike') }}"
       id="comment-like"
       data-entity-id="{{ comment.id }}"
       data-csrf-token="{{ csrf_token('comment' ~ comment.id) }}"
       data-liked="{{ comment.commentLikes.isEmpty == true ? '0' : '1' }}"
       data-likes-counter="{{ comment.commentLikes.count }}"
       class="btn-like">
        <i class="♡ {{ comment.commentLikes.isEmpty == true ? '' : 'liked' }}">
            {{ comment.commentLikes.isEmpty == true ? '♡' : '♥' }}
        </i>
    </a>
    <span class="counter">{{ comment.commentLikes.count }}</span>
</div>

{% block javascripts %}
    {{ parent() }}
    <script src="{{ asset('js/comments_like_unlike.js') }}"></script>
{% endblock %}

comments_like_unlike.js

$(document).on('click', '#comment-like', function (e) {
    e.preventDefault();

    var url = $(this).attr('href');
    var entityId = $(this).attr('data-entity-id');
    var csrfToken = $(this).attr('data-csrf-token');
    var isLiked = $(this).attr('data-liked');
    var likesCounter = $(this).attr('data-likes-counter');

    if (isLiked === '0') {
        $(this).attr('data-liked', 1);
        $('.♡').addClass('liked').text('♥')
        likesCounter++;
    } else {
        $(this).attr('data-liked', 0);
        $('.♡').removeClass('liked').text('♡')
        likesCounter--;
    }
    $.ajax({
        type: 'POST',
        dataType: 'json',
        data: {'entityId': entityId, 'csrfToken': csrfToken, 'likesCounter': likesCounter},
        url: url,
        success: function () {
            console.log("success");
        },
        error: function () {
        }
    });
});

解决方案及解释

1. 后端返回更新后的点赞数

当前后端仅返回空的JsonResponse,前端无法获取准确的最新点赞数(多人同时操作时,前端本地计算会有误差)。修改控制器,在操作完成后返回当前评论的点赞总数:

// 替换原有的return new JsonResponse();
// 取消点赞逻辑
if($commentAlreadyLiked){
    $entityManager->remove($commentAlreadyLiked);
    $entityManager->flush();
    $likesCount = $comment->getCommentLikes()->count();
    return new JsonResponse(['likesCount' => $likesCount]);
} else{
// 点赞逻辑
    $like = new CommentLike();
    $like->setUser($user);
    $like->setComment($comment);
    $entityManager->persist($like);
    $entityManager->flush();
    $likesCount = $comment->getCommentLikes()->count();
    return new JsonResponse(['likesCount' => $likesCount]);
}

2. 修正前端DOM选择器

当前$('.♡')会选中页面所有爱心图标,应仅操作当前点击按钮内的图标,改用$(this).find('.♡')定位。

3. 更新计数器DOM元素

之前仅修改了变量likesCounter,未更新页面上的<span class="counter">元素,需找到对应元素并更新文本,同时优先使用后端返回的准确数值。

4. 修正计数器变量类型

从data-属性获取的值是字符串类型,直接++/--会导致字符串拼接,需转成整数。

修改后的JS代码

$(document).on('click', '#comment-like', function (e) {
    e.preventDefault();
    const $this = $(this);
    const url = $this.attr('href');
    const entityId = $this.attr('data-entity-id');
    const csrfToken = $this.attr('data-csrf-token');
    let isLiked = $this.attr('data-liked');
    // 转成整数类型
    let likesCounter = parseInt($this.attr('data-likes-counter'), 10);

    // 先临时更新UI提升体验
    if (isLiked === '0') {
        $this.attr('data-liked', '1');
        $this.find('.♡').addClass('liked').text('♥');
        likesCounter++;
    } else {
        $this.attr('data-liked', '0');
        $this.find('.♡').removeClass('liked').text('♡');
        likesCounter--;
    }

    $.ajax({
        type: 'POST',
        dataType: 'json',
        data: {'entityId': entityId, 'csrfToken': csrfToken},
        // 无需传likesCounter给后端,后端自行计算准确值
        url: url,
        success: function (response) {
            // 用后端返回的准确值更新计数器和data属性
            const finalCount = response.likesCount;
            $this.attr('data-likes-counter', finalCount);
            $this.siblings('.counter').text(finalCount);
        },
        error: function () {
            // 请求失败时回滚UI状态
            if (isLiked === '0') {
                $this.attr('data-liked', '0');
                $this.find('.♡').removeClass('liked').text('♡');
            } else {
                $this.attr('data-liked', '1');
                $this.find('.♡').addClass('liked').text('♥');
            }
            alert('点赞操作失败,请重试');
        }
    });
});

额外优化:避免重复ID

若页面有多个评论,id="comment-like"会重复,建议改为class:class="comment-like btn-like",JS选择器改为$(document).on('click', '.comment-like', ...),避免ID冲突导致仅第一个评论的点赞按钮生效。

内容的提问来源于stack exchange,提问作者Officer Pulaski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 17:05:46