Laravel 8 Multilevel Nested Comments System

  • Share this:
Laravel 8 Multilevel Nested Comments System

Today I am going to discuss the nested comment and reply system in Laravel 8. Hope you will enjoy it.

A comment system is a key feature of any blog or any website. So in this example tutorial, I am going to show you how we can create a comment and reply system in the Laravel 8 application. We will see it from scratch.

Many developers look for a good package for the comment and reply system, but now we are going to create it without any packages. I will create a very simple comment system where you can add a post and then you can leave a comment and can make a reply to any specific post.

I will make a polymorphic relationship to create this comment and reply system. Having completed this tutorial you won't need to use any packages like the Facebook comments plugin or discuss comment plugin to your website.

You can make your own comment system to your website by following this tutorial. I will create a post table and a comment table. So let's start our brand new tutorial nested comment system in laravel 8.

 

Step 1 : Install Laravel  8

Now download laravel 8 to start our project. So run below command to download it.

composer create-project --prefer-dist laravel/laravel blog

 

Step 2: Create Post and Comment Model

We need Post and Comment model to create a comment and reply system. So let's create it. Run below command 

php artisan make:model Post -m

 

Define the schema in the post-migration file.

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

 

Also, we need to create Comment model and migration file, so create using the following command.

php artisan make:model Comment -m

 

Having done it, Now i will do the polymorphic relationship between those models. So i need to define the schema below way

public function up()
{
    Schema::create('comments', function (Blueprint $table) {
       $table->increments('id');
       $table->integer('user_id')->unsigned();
       $table->integer('parent_id')->unsigned();
       $table->text('comment');
       $table->integer('commentable_id')->unsigned();
       $table->string('commentable_type');
       $table->timestamps();
    });
}

 

Now run this below command to migrate our post and comment table.

php artisan migrate

 

Step 3:  Create Auth

As only authenticated user can leave a comment. So we need to create auth in our application. So let's make it.You need to follow some few steps to complete auth in your laravel 7 application. First you need to install laravel/ui package.

composer require laravel/ui

 

Now you can use following commands for creating auth:

php artisan ui bootstrap --auth

 

Step 4:  Create Relationship

Now, we need to define the Polymorphic relationships and other relationship which will be needed in this tutorial. So do it. 

app/Post.php


namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $guarded = [];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

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

}

 

app/Comment.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{   

    protected $guarded = [];
    
    public function user()
    {
        return $this->belongsTo(User::class);
    }
    
    public function replies()
    {
        return $this->hasMany(Comment::class, 'parent_id');
    }
}

 

app/User.php 

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    public function posts() {
  
        return $this->hasMany(Post::class);
     
    }
}

 

Step 5: Create Controller

In this step, now we should create new controller as PostController and CommentController. So run bellow command and create new controller

php artisan make:controller PostController

 

and now paste this below code to your post controller.

app/Http/Controllers/PostController.php

namespace App\Http\Controllers;

use App\Post;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class PostController extends Controller
{
    public function __construct()
    {
       $this->middleware('auth');
    }
    
    public function index()
    {
       $posts = Post::take(5)->get();

       return view('post.index', compact('posts'));
    }

    public function create()
    {
        return view('post.create');
    }

    public function store(Request $request)
    {

        $validator = Validator::make($request->all(), [
            'title' => 'required|min:3',
        ]);

        if ($validator->fails()) {

            return redirect('post')
                        ->withErrors($validator)
                        ->withInput();
        }

        Post::create([
            'title' => $request->title,
            'slug' => \Str::slug($request->title)
        ]);

        return redirect()->back();

    }

    public function show(Post $post) {

        return view('post.single',compact('post'));

    }
}

 

And now we have to create our comment controller. So now create it.

php artisan make:controller CommentController

 

and now paste this below code to your comments controller.

app/Http/Controllers/CommentController.php

namespace App\Http\Controllers;

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

class CommentController extends Controller
{
    public function store(Request $request)
    {
        $comment = new Comment;

        $comment->comment = $request->comment;

        $comment->user()->associate($request->user());

        $post = Post::find($request->post_id);

        $post->comments()->save($comment);

        return back();
    }

    public function replyStore(Request $request)
    {
        $reply = new Comment();

        $reply->comment = $request->get('comment');

        $reply->user()->associate($request->user());

        $reply->parent_id = $request->get('comment_id');

        $post = Post::find($request->get('post_id'));

        $post->comments()->save($reply);

        return back();

    }
}

 

Step 6:  Create Routes

Now we have to create our routes to view our comment and reply file. Paste this below code to your web.php.

routes/web.php

Route::get('post', 'PostController@create')->name('post.create');
Route::post('post', 'PostController@store')->name('post.store');
Route::get('/posts', 'PostController@index')->name('posts');
Route::get('/article/{post:slug}', 'PostController@show')->name('post.show');
Route::post('/comment/store', 'CommentController@store')->name('comment.add');
Route::post('/reply/store', 'CommentController@replyStore')->name('reply.add');

 

Step 7:  Create Blade Files

In the last step. In this step, we have to create blade files. So we have to create a post folder and in the post folder, we will create our view file.

resources/views/post/index.blade.php


@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <table class="table table-striped">
                <thead>
                    <th>No</th>
                    <th>Title</th>
                    <th>Action</th>
                </thead>
                <tbody>
                @foreach($posts as $post)
                <tr>
                    <td>{{ $post->id }}</td>
                    <td>{{ $post->title }}</td>
                    <td>
                        <a href="{{ route('post.show',$post->slug) }}" class="btn btn-sm btn-outline-danger py-0" style="font-size: 0.8em;">Read Post</a>
                    </td>
                </tr>
                @endforeach
                </tbody>

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

 

resources/views/post/create.blade.php


@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Create Post</div>
                <div class="card-body">

                    @error('title')
                        <div class="alert alert-danger">{{ $message }}</div>
                    @enderror
                    
                    <form method="post" action="{{ route('post.store') }}">
                        <div class="form-group">
                            @csrf
                            <label class="label">Post Title: </label>
                            <input type="text" name="title" class="form-control" required/>
                        </div>
                        <div class="form-group">
                            <input type="submit" class="btn btn-success" value="Create post"/>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection 

 

resources/views/post/single.blade.php


@extends('layouts.app')
<style>
    .display-comment .display-comment {
        margin-left: 40px
    }
</style>
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-body">
                    <p>{{ $post->title }}</p>
                </div>
      
               <div class="card-body">
                <h5>Display Comments</h5>
            
                @include('post.partials.replies', ['comments' => $post->comments, 'post_id' => $post->id])

                <hr />
               </div>

               <div class="card-body">
                <h5>Leave a comment</h5>
                <form method="post" action="{{ route('comment.add') }}">
                    @csrf
                    <div class="form-group">
                        <input type="text" name="comment" class="form-control" />
                        <input type="hidden" name="post_id" value="{{ $post->id }}" />
                    </div>
                    <div class="form-group">
                        <input type="submit" class="btn btn-sm btn-outline-danger py-0" style="font-size: 0.8em;" value="Add Comment" />
                    </div>
                </form>
               </div>

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

 

resources/views/post/partials/replys.blade.php


@foreach($comments as $comment)
<div class="display-comment">
    <strong>{{ $comment->user->name }}</strong>
    <p>{{ $comment->comment }}</p>
    <a href="" id="reply"></a>
    <form method="post" action="{{ route('reply.add') }}">
        @csrf
        <div class="form-group">
            <input type="text" name="comment" class="form-control" />
            <input type="hidden" name="post_id" value="{{ $post_id }}" />
            <input type="hidden" name="comment_id" value="{{ $comment->id }}" />
        </div>
        <div class="form-group">
            <input type="submit" class="btn btn-sm btn-outline-danger py-0" style="font-size: 0.8em;" value="Reply" />
        </div>
    </form>
    @include('post.partials.replies', ['comments' => $comment->replies])
</div>
@endforeach 

About author
I am a professional web developer. I love programming and coding, and reading books. I am the founder and CEO of StorialTech.