LessonsBuild a Blog → Part 8

Comments System

Laravel
⏱ 22 min read🏗️ ProjectNot completed

Let's add a comments system where readers can leave comments on posts. Comments will require moderation (approval) before appearing publicly.

Comment Controller

app/Http/Controllers/CommentController.php
<?php
namespace App\Http\Controllers;

use App\Models\Post;
use App\Models\Comment;
use Illuminate\Http\Request;

class CommentController extends Controller
{
    public function store(Request $request, Post $post)
    {
        $validated = $request->validate([
            'body'         => 'required|string|min:5|max:1000',
            'author_name'  => 'required_if:guest,true|string|max:100',
            'author_email' => 'required_if:guest,true|email|max:255',
        ]);

        Comment::create([
            'post_id'      => $post->id,
            'user_id'      => auth()->id(),
            'author_name'  => auth()->check() ? null : $validated['author_name'],
            'author_email' => auth()->check() ? null : $validated['author_email'],
            'body'         => $validated['body'],
            'approved'     => auth()->check(), // Auto-approve for logged-in users
        ]);

        $message = auth()->check()
            ? 'Comment posted successfully!'
            : 'Comment submitted! It will appear after moderation.';

        return back()->with('success', $message);
    }
}

Comment Routes

routes/web.php
<?php
Route::post('/post/{post}/comments', [CommentController::class, 'store'])
    ->name('comments.store')
    ->middleware('throttle:5,1'); // Max 5 comments per minute

Comments in Post View

resources/views/blog/show.blade.php (comments section)
<!-- Comments Section -->
<div class="mt-12">
    <h3 class="text-xl font-bold mb-6">
        {{ $post->approvedComments->count() }} Comments
    </h3>

    <!-- Comment List -->
    @forelse($post->approvedComments as $comment)
    <div class="flex gap-4 mb-6">
        <div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center font-bold text-blue-600 flex-shrink-0">
            {{ strtoupper(substr($comment->display_name, 0, 1)) }}
        </div>
        <div class="flex-1">
            <div class="flex items-center gap-2 mb-1">
                <strong>{{ $comment->display_name }}</strong>
                <span class="text-gray-400 text-xs">{{ $comment->created_at->diffForHumans() }}</span>
            </div>
            <p class="text-gray-700">{{ $comment->body }}</p>
        </div>
    </div>
    @empty
    <p class="text-gray-400">No comments yet. Be the first!</p>
    @endforelse

    <!-- Comment Form -->
    <div class="mt-8 bg-gray-50 rounded-lg p-6">
        <h4 class="font-bold mb-4">Leave a Comment</h4>

        @if(session('success'))
            <div class="bg-green-100 text-green-700 p-3 rounded mb-4">{{ session('success') }}</div>
        @endif

        <form method="POST" action="{{ route('comments.store', $post) }}">
            @csrf

            @guest
            <div class="grid grid-cols-2 gap-4 mb-4">
                <div>
                    <label class="block text-sm font-medium mb-1">Name *</label>
                    <input type="text" name="author_name" class="w-full border rounded p-2"
                           value="{{ old('author_name') }}" required />
                </div>
                <div>
                    <label class="block text-sm font-medium mb-1">Email *</label>
                    <input type="email" name="author_email" class="w-full border rounded p-2"
                           value="{{ old('author_email') }}" required />
                </div>
            </div>
            @endguest

            <div class="mb-4">
                <label class="block text-sm font-medium mb-1">Comment *</label>
                <textarea name="body" rows="4" class="w-full border rounded p-2"
                          placeholder="Share your thoughts...">{{ old('body') }}</textarea>
                @error('body')<p class="text-red-500 text-sm mt-1">{{ $message }}</p>@enderror
            </div>

            <button type="submit"
                    class="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700">
                Post Comment
            </button>
        </form>
    </div>
</div>

Admin — Approve Comments

app/Http/Controllers/Admin/CommentController.php
<?php
class CommentController extends Controller
{
    public function index()
    {
        $pending  = Comment::where('approved', false)->with('post')->latest()->get();
        $approved = Comment::where('approved', true)->with('post')->latest()->take(20)->get();
        return view('admin.comments.index', compact('pending', 'approved'));
    }

    public function update(Comment $comment)
    {
        $comment->update(['approved' => true]);
        return back()->with('success', 'Comment approved!');
    }

    public function destroy(Comment $comment)
    {
        $comment->delete();
        return back()->with('success', 'Comment deleted!');
    }
}
💡
Always use throttle on comment forms! ->middleware('throttle:5,1') limits to 5 submissions per minute per IP. This prevents spam bots from flooding your blog with fake comments.
← Previous Next Lesson →
💡

Stuck? Need help?

Review the previous lessons or check the Laravel documentation.

Laravel Docs →