处理回复评论表单的提交-Symfony5全面开发

您需要登录后才可观看此视频

为了更好的为您提供服务,请您登录后再查看本课程。

立即登录

在回复表单上点击右键检查一下表单代码,回复表单的action地址就是我们的reply_comment路由的地址。我们需要在replyComment()方法中,来处理表单的提交。

使用Form对象的handleRequest()方法来处理表单的请求,添加一个条件判断。如果回复表单已经提交了,isSubmitted(),并且回复表中的CSRFtoken是正确的。我们来获取表单的数据,输入一下dd(),查看一下表单的数据。

回到浏览器,我们手动的填写一下表单,表单提交的数据自动的转换为了Comment对象。Comment对象有作者、邮箱、消息。对于子级评论,我们可以不设置评论的post属性,但是我们要设置parent属性,用来设置父级评论对象。

回到代码,添加一个注释,$data数据是一个Comment对象,我们设置一下评论的父级对象,就是$parentComment。然后我们注入EntityManager对象,保存Comment数据,提交一下数据。保存完表单提交的数据之后,我们需要让页面跳转到文章的详情页。使用redirectToRoute()跳转到post_show路由,需要传递一个参数id1,参数的值就是Post对象的id。

#src/Controller/CommentController.php
class CommentController extends AbstractController
{
    #[
        Route('/post/{post_id}/comment/{comment_id}/reply',options: ['expose' => true], name: 'reply_comment'),
        ParamConverter('post', options: ['id' => 'post_id']),
        ParamConverter('parentComment', options: ['id' => 'comment_id']),
    ]
    public function replyComment(Request $request, Post $post, Comment $parentComment, EntityManagerInterface $em): Response
    {
        $replyComment = $this->createForm(CommentType::class, null, [
            'action' => $request->getUri()
        ]);

        $replyComment->handleRequest($request);

        if ($replyComment->isSubmitted() && $replyComment->isValid()){
            /**@var Comment $data**/
            $data = $replyComment->getData();
            $data->setParent($parentComment);
            $data->setPost($post);
            $em->persist($data);
            $em->flush();

            return $this->redirectToRoute('post_show', ['id1' => $post->getId()]);
        }

        return $this->render('comment/_reply_comment_form.html.twig', [
            'reply_comment_form' => $replyComment->createView(),
        ]);
    }
}

回到浏览器,刷新再次提交表单,现在提示错误了,评论对象的文章id不能设置为空,那我们设置一下子评论的文章属性。刷新再次提交数据,现在子评论就成功的插入到了数据库中,但是我们提交的子评论并没有嵌套显示在父评论下方。

回到项目,我们打开show.html.twig文件,我们把之前嵌套显示的评论注释掉了,我们看注释的代码第71行,71行是条新的评论,它在父级评论的消息正文后面,我们复制71行到77行代码。拷贝,粘贴到回复按钮下方,取消代码的注释,我们打开Comment类。

在Comment类中,我们可以通过getChildren()方法来获取当前评论的所有子评论,回到show.html.twig文件,我们使用comment的getChildren()方法来获取所有子评论。

使用for循环来遍历所有子评论,{% for childComment in comment.children %},结束子评论,然后我们修改子评论的作者和子评论的内容。然后在子评论的消息后面,我们也添加一个回复按钮,回复按钮的文章id不变,但是data-parent-id属性就是我们子评论的id属性。

但是现在还有一个问题,如果子评论它也有下级评论,该怎么办呢?我们还需要再复制代码吗?这样代码就无限的复制了。

回到浏览器打开Twig文档,twig.symfony.com,打开文档,Symfony为我们提供了一个macro关键字。我们查看macro关键字的文档,macro关键字可以定义一个方法,在方法中显示html代码,我们还可以在macro的方法中进行自身的递归调用。

回到项目代码,在templates目录中我们新添加一个macro.html.twig文件,在macro.html.twig文件中,我们可以定一个macro方法,方法名称叫做show_comments()。它是一个方法,需要一个括号,然后我们结束一下macro。

我们回到show.html.twig文件,我们首先获取文章的所有评论,作为父级评论,我们把for循环中的代码复制一下,粘贴到macro的show_comments()方法中。我们需要传入一个参数,在show_comments()方法中,我们用到了comment变量,这个变量需要我们通过参数来传入,我们还用到了post变量,也需要使用参数传入。在第8行,通过for循环的方式显示所有的子评论,我们可以在第8行的循环体中再次调用自身的show_comments()方法,实现一个递归调用。

在第二行前添加一行代码,我们使用import关键字,将marco.html.twig文件引入到show_comments()方法中,_self关键字代表macro.html.twig文件。引入后作为macro变量,在第9行的循环体中,我们删除代码。直接调用macroshow_comments()方法。

第一个参数我们传入当前子评论对象,第二个参数我们传入post对象,这样当在子循环中调用show_comments()方法时,它会传入子评论对象和文章对象再次显示评论数据。如果遇到评论数据还有子对象,那么将会再次递归调用show_comments()方法,我们回到show.html.twig文件,删除放循环中的代码。

#templates/marco.html.twig

{% macro show_comments(comment, post) %}
    {% import _self as macro %}
    <div class="media mb-4">
        <img class="d-flex mr-3 rounded-circle" src="http://placehold.it/50x50" alt="">
        <div class="media-body">
            <h5 class="mt-0">{{ comment.author }}</h5>
            {{ comment.message }}
            <button class="btn btn-sm btn-link js-reply-comment-btn" data-post-id="{{ post.id }}" data-parent-id="{{ comment.id }}">回复</button>
            {% for childComment in comment.children %}
                {{ macro.show_comments(childComment, post) }}
            {% endfor %}
        </div>
    </div>
{% endmacro %}

首先我们在文件顶部引入macro.html.twig文件,然后作为变量macros。在评论的for循环体中,我们使用macros变量的show_comments()方法来递归的显示评论列表。第一个参数我们传入comment,第二个参数我们传入post。

#templates/post/show.html.twig

{% extends 'base.html.twig' %}
{% import 'marco.html.twig' as macros %}

<!-- Single Comment -->
{% for comment in post.comments %}
    {{ macros.show_comments(comment, post) }}
{% endfor %}

回到文章详情页刷新,现在子评论就按照递归的方式显示了,我们再添加一条评论。提交,再次查看评论列表,出现了问题。我们新添加的评论已经显示在对应的位置了,但是在评论的列表前,它又进行了额外的显示。这是因为所有的子评论对象,它们也都设置了post属性,那么在post获取所有评论对象时,将所有的子评论当做顶级评论对象进行了遍历。

另外我们点击回复按钮,添加了一个回复评论框,当再次点击时,在评论框下方又多了一条评论框,我们想让评论表单只显示一次。

在下节课我们将解决这些问题。

课程讨论

当前内容评论功能已关闭。