Laravel 11 Comment System with Replies Example

By Hardik Savani June 20, 2024 Category : Laravel

In this tutorial, we will learn how to create comment system in laravel 11 application.

A Comment system is a primary requirement for blog website or any tutorial website. A comment system on a blog website lets readers share their thoughts, ask questions, and interact with the author and other readers. It builds community, provides valuable feedback, and can increase engagement on the site. Comments make the content more dynamic and engaging, enhancing the reader's experience and encouraging them to return. They also offer the author insights into their audience’s interests and concerns, helping improve future content.

In this example, we will install Laravel UI to make a basic login and register system. Then, we will create a table for posts with columns for the title and body. A user can register and make a post. Other users can see the post and add a comment. We will also add a feature so users can reply to comments. We will use Laravel's relationships to make the comment system work, using hasMany() and belongsTo(). Just follow the steps below to do this example.

You can see the preview of examples:

Post Detail Page:

Post Page:

Step for How to Create Comment System in Laravel 11?

  • Step 1: Install Laravel 11
  • Step 2: Create Auth using Scaffold
  • Step 3: Create Posts and Comments Tables
  • Step 4: Create Models
  • Step 5: Create Routes
  • Step 6: Create Controller
  • Step 7: Create and Update Blade Files
  • Run Laravel App

Step 1: Install Laravel 11

This step is not required; however, if you have not created the Laravel app, then you may go ahead and execute the below command:

composer create-project laravel/laravel example-app

Step 2: Create Auth using Scaffold

Now, in this step, we will create an auth scaffold command to generate login, register, and dashboard functionalities. So, run the following commands:

Laravel 11 UI Package:

composer require laravel/ui 

Generate Auth:

php artisan ui bootstrap --auth 

npm install

npm run build

Step 3: Create Posts and Comments Tables

Here, we will create posts and comments table. so, let's run the following command:

php artisan make:migration create_posts_comments_table

now, let's update the following migrations:

database/migrations/2024_06_19_140622_create_posts_comments_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });

        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->integer('user_id')->unsigned();
            $table->integer('post_id')->unsigned();
            $table->integer('parent_id')->unsigned()->nullable();
            $table->text('body');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
        Schema::dropIfExists('comments');
    }
};

now, Let's run the migration command:

php artisan migrate

Step 4: Create Models

Here, we will create Post and Comment model using the following command. we also need to update User model here. we will write relationship and some model function for Comment system.

php artisan make:model Post

now, update the model file with hasMany() relationship:

app/Models/Post.php

<?php

namespace App\Models;

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

class Post extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['title', 'body'];
   
    /**
     * The has Many Relationship
     *
     * @var array
     */
    public function comments()
    {
        return $this->hasMany(Comment::class)->whereNull('parent_id')->latest();
    }
}

app/Models/Comment.php

<?php

namespace App\Models;

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

class Comment extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['user_id', 'post_id', 'parent_id', 'body'];
   
    /**
     * The belongs to Relationship
     *
     * @var array
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
   
    /**
     * The has Many Relationship
     *
     * @var array
     */
    public function replies()
    {
        return $this->hasMany(Comment::class, 'parent_id');
    }
}

Step 5: Create Routes

In this step, we need to create some routes for comment system. So open your "routes/web.php" file and add the following route.

routes/web.php

<?php

use Illuminate\Support\Facades\Route;

use App\Http\Controllers\PostController;

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');

Route::middleware('auth')->group(function () {
    Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
    Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
    Route::get('/posts/{id}', [PostController::class, 'show'])->name('posts.show');
    Route::post('/posts/comment/store', [PostController::class, 'commentStore'])->name('posts.comment.store');
});

Step 6: Create Controller

Here, we require the creation of a new controller, PostController, with an index method to display comment. So let's put the code below.

app/Http/Controllers/PostController.php

<?php
   
namespace App\Http\Controllers;
   
use Illuminate\Http\Request;
use App\Models\Post;
use App\Models\Comment;
   
class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $posts = Post::latest()->get();
    
        return view('posts.index', compact('posts'));
    }
    
    /**
     * Write code on Method
     *
     * @return response()
     */
    public function store(Request $request)
    {
        $this->validate($request, [
             'title' => 'required',
             'body' => 'required'
        ]);
   
        $post = Post::create([
            'title' => $request->title,
            'body' => $request->body
        ]);
   
        return back()->with('success','Post created successfully.');
    }
    
    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $post = Post::find($id);
        return view('posts.show', compact('post'));
    }

    /**
     * Write code on Method
     *
     * @return response()
     */
    public function commentStore(Request $request)
    {
        $request->validate([
            'body'=>'required',
        ]);
   
        $input = $request->all();
        $input['user_id'] = auth()->user()->id;
    
        Comment::create($input);
   
        return back()->with('success','Comment added successfully.');
    }
}

Step 7: Create and Update Blade Files

In this step, we will update app.blade.php file and create posts.blade file. so, let's update it.

resources/views/layouts/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.bunny.net">
    <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">

    <!-- Scripts -->
    @vite(['resources/sass/app.scss', 'resources/js/app.js'])

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />

    <style type="text/css">
        .img-user{
            width: 40px;
            border-radius: 50%;
        }
        .col-md-1{
            padding-right: 0px !important;
        }
        .img-col{
            width: 5.33% !important;
        }
    </style>
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    Laravel Comment System Example - ItSolutionStuff.com
                </a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav me-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ms-auto">
                        <!-- Authentication Links -->
                        @guest
                            @if (Route::has('login'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                                </li>
                            @endif

                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('posts.index') }}">{{ __('Posts') }}</a>
                            </li>
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }}
                                </a>

                                <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4">
            @yield('content')
        </main>
    </div>
</body>
</html>

resources/views/posts/index.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header"><i class="fa fa-list"></i> {{ __('Posts List') }}</div>

                <div class="card-body">

                    @session('success')
                        <div class="alert alert-success" role="alert"> 
                            {{ $value }}
                        </div>
                    @endsession
                    
                    <p><strong>Create New Post</strong></p>
                    <form method="post" action="{{ route('posts.store') }}" enctype="multipart/form-data">
                        @csrf
                        <div class="form-group">
                            <label>Title:</label>
                            <input type="text" name="title" class="form-control" />
                            @error('title')
                                <div class="text-danger">{{ $message }}</div>
                            @enderror
                        </div>
                        <div class="form-group">
                            <label>Body:</label>
                            <textarea class="form-control" name="body"></textarea>
                            @error('body')
                                <div class="text-danger">{{ $message }}</div>
                            @enderror
                        </div>
                        <div class="form-group mt-2">
                            <button type="submit" class="btn btn-success btn-block"><i class="fa fa-save"></i> Submit</button>
                        </div>
                    </form>

                    <p class="mt-4"><strong>Post List:</strong></p>
                    
                    @foreach($posts as $post)
                        <div class="card mt-2">
                          <div class="card-body">
                            <h5 class="card-title">{{ $post->title }}</h5>
                            <p class="card-text">{{ $post->body }}</p>
                            <div class="text-end">
                                <a href="{{ route('posts.show', $post->id) }}" class="btn btn-primary">View</a>
                            </div>
                          </div>
                        </div>
                    @endforeach

                </div>
            </div>
        </div>
    </div>
</div>
@endsection

resources/views/posts/show.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-12">
            <div class="card">
                <div class="card-header"><h1><i class="fa fa-thumbs-up"></i> {{ $post->title }}</h1></div>

                <div class="card-body">

                    @session('success')
                        <div class="alert alert-success" role="alert"> 
                            {{ $value }}
                        </div>
                    @endsession

                    {{ $post->body }}

                    <h4 class="mt-4">Comments:</h4>

                    <form method="post" action="{{ route('posts.comment.store') }}">
                        @csrf
                        <div class="form-group">
                            <textarea class="form-control" name="body" placeholder="Write Your Comment..."></textarea>
                            <input type="hidden" name="post_id" value="{{ $post->id }}" />
                        </div>
                        <div class="form-group text-end">
                            <button class="btn btn-success mt-2"><i class="fa fa-comment"></i> Add Comment</button>
                        </div>
                    </form>
                    
                    <hr/>
                    @include('posts.comments', ['comments' => $post->comments, 'post_id' => $post->id])
                    
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

resources/views/posts/comments.blade.php

@foreach($comments as $comment)
    <div class="display-comment row mt-3" @if($comment->parent_id != null) style="margin-left:40px;" @endif>
        <div class="col-md-1 text-end img-col">
            <img src="https://randomuser.me/api/portraits/men/43.jpg" class="img-user">
        </div>
        <div class="col-md-11">
            <strong>{{ $comment->user->name }}</strong>
            <br/><small><i class="fa fa-clock"></i> {{ $comment->created_at->diffForHumans() }}</small>
            <p>{!! nl2br($comment->body) !!}</p>
            <form method="post" action="{{ route('posts.comment.store') }}">
                @csrf
                <div class="row">
                    <div class="col-md-11">
                        <div class="form-group">
                            <textarea class="form-control" name="body" placeholder="Write Your Reply..." style="height: 40px;"></textarea>
                            <input type="hidden" name="post_id" value="{{ $post_id }}" />
                            <input type="hidden" name="parent_id" value="{{ $comment->id }}" />
                        </div>
                    </div>
                    <div class="col-md-1">
                        <button class="btn btn-warning"><i class="fa fa-reply"></i> Reply</button>
                    </div>
                </div>
            </form>
            @include('posts.comments', ['comments' => $comment->replies])
        </div>
    </div>
@endforeach

Run Laravel App:

All the required steps have been done, now you have to type the given below command and hit enter to run the Laravel app:

php artisan serve

Now, Go to your web browser, type the given URL and view the app output:

http://localhost:8000/

Now, you can register as user and create post. Then comment on it.

You can download the code from git:

Download Code from Github

I hope it can help you...

Shares