Laravel 11 Comment System with Replies Example
Laravel 11 Comment System with Replies Example

Laravel 11 Comment System with Replies Example

In this tutorial, we will build a comment system with replies using Laravel 11. This system allows users to add comments and replies to comments, creating a threaded discussion. Let’s walk through the steps to create this feature in a simple and understandable way.

Step 1: Set Up Laravel Project

First, you need to set up a new Laravel project. If you haven’t installed Laravel yet, you can do so by following the official documentation.

composer create-project --prefer-dist laravel/laravel comment-system
cd comment-system

Step 2: Set Up Database

Configure your database in the .env file. Update the following lines with your database details:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_database_name
DB_USERNAME=your_database_user
DB_PASSWORD=your_database_password

Run the migration to create the necessary tables:

php artisan migrate

Step 3: Create Models and Migrations

We need two models: Post and Comment. The Post model represents a blog post, and the Comment model represents comments and replies.

Create the Post model and migration:

php artisan make:model Post -m

Create the Comment model and migration:

php artisan make:model Comment -m

Update the posts migration file to create the posts table:

// database/migrations/xxxx_xx_xx_create_posts_table.php

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->timestamps();
    });
}

Update the comments migration file to create the comments table:

// database/migrations/xxxx_xx_xx_create_comments_table.php

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
        $table->id();
        $table->foreignId('post_id')->constrained()->onDelete('cascade');
        $table->foreignId('parent_id')->nullable()->constrained('comments')->onDelete('cascade');
        $table->text('content');
        $table->timestamps();
    });
}

Run the migrations:

php artisan migrate

Step 4: Define Relationships in Models

Update the Post model to define the relationship with comments:

// app/Models/Post.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'content'];

    public function comments()
    {
        return $this->hasMany(Comment::class)->whereNull('parent_id');
    }
}

Update the Comment model to define the relationship with posts and replies:

// app/Models/Comment.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{
    use HasFactory;

    protected $fillable = ['post_id', 'parent_id', 'content'];

    public function post()
    {
        return $this->belongsTo(Post::class);
    }

    public function replies()
    {
        return $this->hasMany(Comment::class, 'parent_id');
    }
}

Step 5: Create Controllers

We need a controller for handling posts and comments. Create a PostController:

php artisan make:controller PostController

Update the PostController to handle displaying posts and adding comments:

// app/Http/Controllers/PostController.php

namespace App\Http\Controllers;

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

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::all();
        return view('posts.index', compact('posts'));
    }

    public function show(Post $post)
    {
        return view('posts.show', compact('post'));
    }

    public function storeComment(Request $request, Post $post)
    {
        $request->validate(['content' => 'required']);

        $post->comments()->create([
            'content' => $request->content,
            'parent_id' => $request->parent_id
        ]);

        return back();
    }
}

Step 6: Define Routes

Define routes for displaying posts and adding comments in web.php:

// routes/web.php

use App\Http\Controllers\PostController;

Route::get('/', [PostController::class, 'index']);
Route::get('/posts/{post}', [PostController::class, 'show']);
Route::post('/posts/{post}/comments', [PostController::class, 'storeComment'])->name('comments.store');

Step 7: Create Views

Create a view for displaying posts and comments. First, create a layout file:

<!-- resources/views/layouts/app.blade.php -->

<!DOCTYPE html>
<html>
<head>
    <title>Comment System</title>
    <link rel="stylesheet" href="{{ asset('css/app.css') }}">
</head>
<body>
    <div class="container">
        @yield('content')
    </div>
</body>
</html>

Create a view for listing posts:

<!-- resources/views/posts/index.blade.php -->

@extends('layouts.app')

@section('content')
    <h1>Posts</h1>
    @foreach($posts as $post)
        <div>
            <h2><a href="{{ url('/posts', $post->id) }}">{{ $post->title }}</a></h2>
            <p>{{ $post->content }}</p>
        </div>
    @endforeach
@endsection

Create a view for displaying a single post and its comments:

<!-- resources/views/posts/show.blade.php -->

@extends('layouts.app')

@section('content')
    <h1>{{ $post->title }}</h1>
    <p>{{ $post->content }}</p>

    <h2>Comments</h2>
    <form action="{{ route('comments.store', $post) }}" method="POST">
        @csrf
        <textarea name="content" rows="3" required></textarea>
        <input type="hidden" name="parent_id" value="">
        <button type="submit">Add Comment</button>
    </form>

    @foreach($post->comments as $comment)
        <div style="margin-left: 20px;">
            <p>{{ $comment->content }}</p>
            <form action="{{ route('comments.store', $post) }}" method="POST" style="margin-left: 20px;">
                @csrf
                <textarea name="content" rows="2" required></textarea>
                <input type="hidden" name="parent_id" value="{{ $comment->id }}">
                <button type="submit">Reply</button>
            </form>
            @include('posts.partials.comments', ['comments' => $comment->replies])
        </div>
    @endforeach
@endsection

Create a partial view for displaying nested comments:

<!-- resources/views/posts/partials/comments.blade.php -->

@foreach($comments as $comment)
    <div style="margin-left: 20px;">
        <p>{{ $comment->content }}</p>
        <form action="{{ route('comments.store', $post) }}" method="POST" style="margin-left: 20px;">
            @csrf
            <textarea name="content" rows="2" required></textarea>
            <input type="hidden" name="parent_id" value="{{ $comment->id }}">
            <button type="submit">Reply</button>
        </form>
        @include('posts.partials.comments', ['comments' => $comment->replies])
    </div>
@endforeach

Step 8: Run the Application

You can now run your application and test the comment system:

php artisan serve

Visit http://localhost:8000 in your browser, and you should see the list of posts. Click on a post to view its details and add comments and replies.

Conclusion

In this tutorial, we have created a simple comment system with replies in Laravel 11. We covered setting up the project, defining models and relationships, creating controllers, defining routes, and building views. This should give you a good starting point to build more complex comment systems with nested replies.