Laravel评论编辑/删除误改帖子问题及嵌套回复实现求助
问题描述
正在开发一个帖子与评论系统,已创建Comment和Post控制器及模型。当登录用户(含管理员)编辑或删除评论时,实际被修改的却是对应的帖子,而非评论。相关代码如下:
Comment 控制器代码
public function edit(Comment $comment) { if (auth()->user()->is_admin !== 1) { if ($comment->user_id !== Auth::id()) { abort(403); } } return view('comments.edit', ['comment' => $comment]); } public function destroy(Comment $comment) { if (auth()->user()->is_admin !== 1) { if ($comment->user_id !== Auth::id()) { abort(403); } } // dd($comment); $comment->delete(); return redirect('posts/' . $comment->post->slug)->with('success', 'Comment deleted successfuly'); } public function store(Request $request, Comment $comment, $slug) { $request->validate([ 'body' => 'required' ]); $post_id = Post::where('slug', $slug)->pluck('id'); // dd($post_id[0]); $comment->create([ 'user_id' => $request->user()->id, 'post_id' => $post_id[0], 'body' => $request->input('body'), ]); return redirect()->back(); } // update public function update( Comment $comment,Post $post) { $comment->update([ 'body' => request()->body ]); return redirect('posts/' . $comment->post->slug)->with('success', 'Comment updated successfuly'); }
评论表单代码
<x-panel > <form action="{{route('comments.store', $post)}}" method="POST"> @csrf <div class="mt-6"> <textarea name="body" cols="30" rows="" class="rounded w-full text-sm overflow-y-scroll" placeholder="Comment as {{auth()->user()->name}}" required></textarea> </div> <div class="flex justify-end border-gray-200 pt-3"> <button type="submit" class="bg-gray-600 text-white uppercase font-semibold text-md px-10 rounded-2xl hover:bg-gray-800 hover:text-white">Post</button> </div> </form> </x-panel>
路由代码
Route::get('/posts',[PostController::class,'index'])->name('posts.index'); Route::get('/myposts',[PostController::class,'indexx'])->name('profileposts'); Route::get('/posts/{post:slug}', [PostController::class, 'show'])->name('posts.show'); Route::get('/posts/create/post', [PostController::class, 'create'])->name('posts.create'); //shows create post form Route::post('/posts/create/post', [PostController::class, 'store'])->name('posts.store'); //saves the created post to the database Route::get('/posts/{post:slug}/edit', [PostController::class, 'edit'])->name('posts.edit'); //shows edit post form Route::put('/posts/{post:slug}/edit', [PostController::class, 'update'])->name('posts.update'); //commits edited post to the database Route::delete('/posts/{post}/', [PostController::class, 'destroy'])->name('posts.destroy'); //deletes post from the database Route::post('/posts/{post:slug}/comments', [CommentController::class, 'store'])->name('comments.store'); //store comment Route::get('posts/comments/{comment:slug}/edit', [CommentController::class, 'edit'])->name('comments.edit'); //edit comment Route::put('posts/comments/{comment:slug}/edit', [CommentController::class, 'update'])->name('comments.update'); //commits edited comment to the database Route::delete('posts/comments/{comment:slug}', [CommentController::class, 'destroy'])->name('comments.destroy');
现寻求解答两个问题:
- 导致评论编辑/删除时误修改帖子的原因是什么?
- 如何实现评论的嵌套回复功能?
问题解答
1. 评论编辑/删除误操作帖子的原因
核心原因:路由匹配顺序与规则冲突
Laravel路由按从上到下顺序匹配,你的Post控制器删除/编辑路由定义在Comment路由之前,且Post路由的通配符会匹配到Comment的请求路径:
- Post删除路由
Route::delete('/posts/{post}/', ...)中,{post}通配符会把/posts/comments/xxx里的comments/xxx识别为Post标识,触发PostController的destroy方法删除帖子。 - Post编辑路由
Route::put('/posts/{post:slug}/edit', ...)同理,会把/posts/comments/xxx/edit里的comments/xxx识别为Post的slug,触发PostController的update方法修改帖子。
次要问题:
- Comment模型可能无
slug字段:路由用{comment:slug}做隐式绑定,但如果Comment表没有slug字段,Laravel无法正确解析Comment实例,加剧匹配异常。 - CommentController的
update方法冗余参数:声明了Post $post参数但路由未传递相关参数,会导致Laravel额外解析Post实例,引发不必要的匹配问题。
2. 实现评论嵌套回复功能的步骤
步骤1:修改Comment表结构
添加parent_id字段标记父评论:
// 创建迁移文件 php artisan make:migration add_parent_id_to_comments_table --table=comments // 迁移文件中添加字段 public function up() { Schema::table('comments', function (Blueprint $table) { $table->unsignedBigInteger('parent_id')->nullable(); $table->foreign('parent_id')->references('id')->on('comments')->onDelete('cascade'); }); } // 执行迁移 php artisan migrate
步骤2:在Comment模型定义关联关系
class Comment extends Model { // 其他代码... // 关联父评论 public function parent() { return $this->belongsTo(Comment::class, 'parent_id'); } // 关联子评论(递归加载子评论) public function children() { return $this->hasMany(Comment::class, 'parent_id')->with('children'); } // 关联所属用户(优化加载) public function user() { return $this->belongsTo(User::class); } }
步骤3:修改CommentController的store方法,支持接收parent_id
public function store(Request $request, $slug) { $request->validate([ 'body' => 'required', 'parent_id' => 'nullable|exists:comments,id' ]); $post = Post::where('slug', $slug)->firstOrFail(); Comment::create([ 'user_id' => $request->user()->id, 'post_id' => $post->id, 'parent_id' => $request->input('parent_id'), 'body' => $request->input('body'), ]); return redirect()->back(); }
注:移除冗余的
Comment $comment参数,直接用模型静态方法创建更简洁。
步骤4:修改视图,支持回复表单与递归渲染嵌套评论
4.1 帖子详情页的评论列表(添加回复按钮与表单)
{{-- 只渲染顶级评论 --}} @foreach($post->comments->whereNull('parent_id') as $comment) <div class="comment"> <p>{{ $comment->user->name }}: {{ $comment->body }}</p> <button onclick="toggleReplyForm({{ $comment->id }})">回复</button> {{-- 回复表单 --}} <div id="reply-form-{{ $comment->id }}" class="hidden mt-2"> <form action="{{ route('comments.store', $post->slug) }}" method="POST"> @csrf <input type="hidden" name="parent_id" value="{{ $comment->id }}"> <textarea name="body" required placeholder="写下你的回复..."></textarea> <button type="submit">提交回复</button> </form> </div> {{-- 递归渲染子评论 --}} @include('comments.children', ['comments' => $comment->children]) </div> @endforeach
4.2 创建子评论局部视图(resources/views/comments/children.blade.php)
@foreach($comments as $comment) <div class="ml-6 mt-2 comment"> <p>{{ $comment->user->name }}: {{ $comment->body }}</p> <button onclick="toggleReplyForm({{ $comment->id }})">回复</button> <div id="reply-form-{{ $comment->id }}" class="hidden mt-2"> <form action="{{ route('comments.store', $comment->post->slug) }}" method="POST"> @csrf <input type="hidden" name="parent_id" value="{{ $comment->id }}"> <textarea name="body" required placeholder="写下你的回复..."></textarea> <button type="submit">提交回复</button> </form> </div> {{-- 递归渲染更深层级的子评论 --}} @include('comments.children', ['comments' => $comment->children]) </div> @endforeach
4.3 添加JS切换回复表单显示状态
<script> function toggleReplyForm(commentId) { const form = document.getElementById(`reply-form-${commentId}`); form.classList.toggle('hidden'); } </script>
步骤5:优化路由(可选)
将Comment路由放在Post路由之前,避免后续匹配问题,同时将{comment:slug}改为{comment}(用默认id绑定即可,无需slug):
// 先定义Comment路由 Route::post('/posts/{post:slug}/comments', [CommentController::class, 'store'])->name('comments.store'); Route::get('posts/comments/{comment}/edit', [CommentController::class, 'edit'])->name('comments.edit'); Route::put('posts/comments/{comment}/edit', [CommentController::class, 'update'])->name('comments.update'); Route::delete('posts/comments/{comment}', [CommentController::class, 'destroy'])->name('comments.destroy'); // 再定义Post路由 Route::get('/posts',[PostController::class,'index'])->name('posts.index'); // ... 其他Post路由
内容的提问来源于stack exchange,提问作者Nelson
相关产品推荐
相关产品推荐

