AppDividend
Latest Code Tutorials

How to Create Comment Nesting in Laravel

In any topic-specific forum, there is always a structure where you need to reply to someone’s comment, and then somebody replies in their comment, and so on. So comment nesting is very useful in any web application, exposing public interest. 

How to Create Comment Nesting in Laravel

To create a comment nesting in Laravel, use the Polymorphic relationship. A one-to-one polymorphic relationship is a case where one model can belong to more than one type of model but on only one association. So, for example, multiple comments belong to multiple users, and a comment can have multiple replies.

Step 1: Install and configure Laravel.

As always, install Laravel using the following command. I am using Laravel Valet.

laravel new comments

# or

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

Go to the project.

cd comments

Open the project in your editor.

code .

Configure the MySQL database in the .env file.

Create an auth using the following command.

php artisan make:auth

Now migrate the database using the following command.

php artisan migrate

Step 2: Create a model and migration.

Create a Post model and migration using the following command.

php artisan make:model Post -m

Define the schema in the post-migration file.

// create_posts_table

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

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

php artisan make:model Comment -m

Okay, now we will use the Polymorphic relationship between the models. So we need to define the schema that way.

// create_comments_table

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

Now, migrate the database using the following cmd.

php artisan migrate

How To Create Comment Nesting In Laravel From Scratch

Step 3: Define Polymorphic Relationships.

Now, we need to define the Polymorphic relationships between the models. So write the following code inside app >> Post.php file. 

<?php

// Post.php 

namespace App;

use Illuminate\Database\Eloquent\Model;

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

Here, we have written all the comments whose parent_id is null. The reason is that we need to display the parent-level comment and also save the parent-level comment. That is why. We need to differentiate between the Comment and its replies.

Post also belongs To a User. So we can define that relationship as well.

<?php

// Post.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
    public function comments()
    {
        return $this->morphMany(Comment::class, 'commentable')->whereNull('parent_id');
    }
}

Define the Comment relationship with the Post. For example, write the following code inside the Comment.php file.

<?php

// Comment.php

namespace App;

use Illuminate\Database\Eloquent\Model;

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

Step 3: Define the views, controller, and routes.

Create a PostController.php file using the following command.

php artisan make:controller PostController

The next step is defining the view route and storing the post in the database. For example, write the following code inside routes >> web.php file.

<?php

// web.php

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

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Route::get('/post/create', 'PostController@create')->name('post.create');
Route::post('/post/store', 'PostController@store')->name('post.store');

Write the following code inside the PostController.php file.

<?php

// PostController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{

    public function __construct()
    {
        return $this->middleware('auth');
    }

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

    public function store(Request $request)
    {
        // store code
    }
}

First, we need to create a form for creating the post. So create a blade file inside resources >> views folder called post.blade.php. Write the following code inside a post.blade.php file.

@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">
                    <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">
                            <label class="label">Post Body: </label>
                            <textarea name="body" rows="10" cols="30" class="form-control" required></textarea>
                        </div>
                        <div class="form-group">
                            <input type="submit" class="btn btn-success" />
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Okay, now go to the resources >> views >> layouts >> app.blade.php file and add a link to create a post.

We need to add the link to the @else part of the navigation bar. So, if the user is successfully logged in, they can create a post; otherwise, they cannot create a post.

@else
     <li class="nav-item">
          <a class="nav-link" href="{{ route('post.create') }}">Create Post</a>
     </li>

Now, go to this link: http://comments.test/register and register a user. After logging in, you can see the Create Post in the navbar. Click that item, and you will redirect to this route: http://comments.test/post/create. Next, you can see our form with the title and body form fields.

Comment Nesting in Laravel 5.6

Step 4: Save and display the Post.

Okay, now we need to save the post in the database, so write the following code inside the store function of the PostController.php file.

<?php

// PostController.php

namespace App\Http\Controllers;
use App\Post;

use Illuminate\Http\Request;

class PostController extends Controller
{

    public function __construct()
    {
        return $this->middleware('auth');
    }

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

    public function store(Request $request)
    {
        $post =  new Post;
        $post->title = $request->get('title');
        $post->body = $request->get('body');

        $post->save();

        return redirect('posts');

    }
}

After saving the post, we are redirecting to the posts list page. We need to define its route too. Add the following route inside a web.php file.

// web.php

Route::get('/posts', 'PostController@index')->name('posts');

Also, we need to define the index function inside the PostController.php file.

// PostController.php

public function index()
{
    $posts = Post::all();

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

Create an index.blade.php file inside the views folder. For example, write the following code inside an index.blade.php file.

@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>ID</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->id) }}" class="btn btn-primary">Show Post</a>
                    </td>
                </tr>
                @endforeach
                </tbody>

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

Now, define the show route inside a web.php file. Add the following line of code inside a web.php file.

// web.php

Route::get('/post/show/{id}', 'PostController@show')->name('post.show');

Also, define the show() function inside the PostController.php file.

// PostController.php

public function show($id)
{
    $post = Post::find($id);

    return view('show', compact('post'));
}

Create a show.blade.php file inside the views folder and add the following code.

@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-body">
                    <p>{{ $post->title }}</p>
                    <p>
                        {{ $post->body }}
                    </p>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Okay, now you can see the individual posts. Fine till now.

The next step is to display the comments on this post.

Laravel Polymorphic morphMany relationship tutorial

Step 5: Create a form to add a comment.

First, create a CommentController.php file using the following command.

php artisan make:controller CommentController

We need to create a form inside a show.blade.php file to add the comment in the particular post.

Write the following code inside a show.blade.php file.

@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-body">
                    <p><b>{{ $post->title }}</b></p>
                    <p>
                        {{ $post->body }}
                    </p>
                    <hr />
                    <h4>Add comment</h4>
                    <form method="post" action="{{ route('comment.add') }}">
                        @csrf
                        <div class="form-group">
                            <input type="text" name="comment_body" class="form-control" />
                            <input type="hidden" name="post_id" value="{{ $post->id }}" />
                        </div>
                        <div class="form-group">
                            <input type="submit" class="btn btn-warning" value="Add Comment" />
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

So, we have added a form that can add the comment. Now, we need to define the route to store the comment.

// web.php

Route::post('/comment/store', 'CommentController@store')->name('comment.add');

Now write the store() function and save the comment using the morphMany() relationship.

<?php

namespace App\Http\Controllers;

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

class CommentController extends Controller
{
    public function store(Request $request)
    {
        $comment = new Comment;
        $comment->body = $request->get('comment_body');
        $comment->user()->associate($request->user());
        $post = Post::find($request->get('post_id'));
        $post->comments()->save($comment);

        return back();
    }
}

Okay, now, if all is well, we can add the comments. Remember, we have not till now displayed the comments. So complete the save functionality, whose parent_id is null.

Laravel Nested Set Example

Step 6: Display the comment.

As we have set up the relationship between a Comment and a Post, we can easily pluck out all the comments related to a particular post.

So, write the following code inside the show.blade.php file. I am writing the whole file to display the comments. Remember, these are the parent comments. We still need to create a reply button and show all the replies.

<!-- show.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-body">
                    <p><b>{{ $post->title }}</b></p>
                    <p>
                        {{ $post->body }}
                    </p>
                    <hr />
                    <h4>Display Comments</h4>
                    @foreach($post->comments as $comment)
                        <div class="display-comment">
                            <strong>{{ $comment->user->name }}</strong>
                            <p>{{ $comment->body }}</p>
                        </div>
                    @endforeach
                    <hr />
                    <h4>Add comment</h4>
                    <form method="post" action="{{ route('comment.add') }}">
                        @csrf
                        <div class="form-group">
                            <input type="text" name="comment_body" class="form-control" />
                            <input type="hidden" name="post_id" value="{{ $post->id }}" />
                        </div>
                        <div class="form-group">
                            <input type="submit" class="btn btn-warning" value="Add Comment" />
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Now, add the comment, and it will show us here in the same url.

Laravel Nesting Relationships

Step 7: Create a Reply form and save replies.

We need to create a function called replies() inside the Comment.php model.

<?php

// Comment.php

namespace App;

use Illuminate\Database\Eloquent\Model;

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

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

Here, we need to add a primary key as a parent_id in the replies function because we need to fetch a reply based on a parent comment’s id.

Okay, now we need to display all the comments and replies code into the partial blade file.

This is because we need to nest the comment replies and how much nesting is required depends upon the user interaction. So we can not predict the nesting levels.

To make it more and more flexible, we need to create partials and then repeat that partial to display the nested comment replies.

First, create a partials folder inside resources >> views folder, and inside the partials folder, create one file called _comment_replies.blade.php.

Write the following code inside the _comment_replies.blade.php file.

<!-- _comment_replies.blade.php -->

 @foreach($comments as $comment)
    <div class="display-comment">
        <strong>{{ $comment->user->name }}</strong>
        <p>{{ $comment->body }}</p>
        <a href="" id="reply"></a>
        <form method="post" action="{{ route('reply.add') }}">
            @csrf
            <div class="form-group">
                <input type="text" name="comment_body" 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-warning" value="Reply" />
            </div>
        </form>
        @include('partials._comment_replies', ['comments' => $comment->replies])
    </div>
@endforeach

Here, I have displayed all the replies with the text box. So it can do further nesting.

Now, this partial is expected to parameters.

  1. comments
  2. post_id.

So, when we include this partial inside the show.blade.php file, we need to pass both of the parameters to access them here.

Also, we need to define the route to save the reply.

Add the following line of code inside routes >> web.php file.

// web.php

Route::post('/reply/store', 'CommentController@replyStore')->name('reply.add');

So, our final web.php file looks like below.

<?php

// web.php

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

Auth::routes();

Route::get('/home', 'HomeController@index')->name('home');

Route::get('/post/create', 'PostController@create')->name('post.create');
Route::post('/post/store', 'PostController@store')->name('post.store');

Route::get('/posts', 'PostController@index')->name('posts');
Route::get('/post/show/{id}', 'PostController@show')->name('post.show');

Route::post('/comment/store', 'CommentController@store')->name('comment.add');
Route::post('/reply/store', 'CommentController@replyStore')->name('reply.add');


Also, define the replyStore() function inside CommentController.php file.

I am writing here the complete code of the CommentController.php file.

<?php

// CommentController.php

namespace App\Http\Controllers;

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

class CommentController extends Controller
{
    public function store(Request $request)
    {
        $comment = new Comment;
        $comment->body = $request->get('comment_body');
        $comment->user()->associate($request->user());
        $post = Post::find($request->get('post_id'));
        $post->comments()->save($comment);

        return back();
    }

    public function replyStore(Request $request)
    {
        $reply = new Comment();
        $reply->body = $request->get('comment_body');
        $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();

    }
}

So, almost both the function store and replyStore function are the same here. This is because we store parent comments and their replies in the same table. But, when we save a parent comment, the parent_id becomes null, and when we store any reply, then parent_id becomes its comment_id. So that is the difference.

Finally, our show.blade.php file looks like this.

<!-- show.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><b>{{ $post->title }}</b></p>
                    <p>
                        {{ $post->body }}
                    </p>
                    <hr />
                    <h4>Display Comments</h4>
                    @include('partials._comment_replies', ['comments' => $post->comments, 'post_id' => $post->id])
                    <hr />
                    <h4>Add comment</h4>
                    <form method="post" action="{{ route('comment.add') }}">
                        @csrf
                        <div class="form-group">
                            <input type="text" name="comment_body" class="form-control" />
                            <input type="hidden" name="post_id" value="{{ $post->id }}" />
                        </div>
                        <div class="form-group">
                            <input type="submit" class="btn btn-warning" value="Add Comment" />
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Here, I have defined the CSS to display proper nesting.

Also, include the partials and pass both of the parameters.

  1. Post comments.
  2. Post id

We can add the parent comment from here but add the partials’ replies.

I have added the parent comment and its replies, and our database table looks like this.

Laravel Nested Set Database

Also, our final output looks like below.

Laravel 5.6 Polymorphic Nested Relationship Example

 

Finally, Create Comment Nesting In Laravel Tutorial With Example is over.

I have put the Github Code of Create Comment Nesting In Laravel so that you can check that out as well.

Github Code

Fork Me On Github

Steps To Use Code

  1. Clone the repository.
  2. Install the dependencies
  3. Configure the database.
  4. Migrate the database using this command: php artisan migrate
  5. Go to the register page and add one user.
  6. Create the post and comment and reply to the comment.
60 Comments
  1. Mr Tee says

    I happened to have struggled with this in my previous project where I had to display user own comments to specific posts but I managed to get it to work until I got this today which I am sure will help with improving the version I had.

    1. Raj Varman says

      yes this site is very interesting bro

      1. Raj Varman says

        I learn many thing from this site

  2. runescape 2007 gold says

    What i ɗo not realize is іf truth ƅe t᧐ld how you are not really a ⅼot more well-favored tһan you migһt be гight now.
    You are so intelligent. You realize ths considerably in relation to
    thiѕ topic, made me foг my part beⅼieve it frօm numerous numerous angles.
    Ӏts like mеn and women аre not involved սnless iit iѕ
    sоmething to accomplish witһ Girl gaga! Your personal stuffs nice.

    Аlways maintain itt up!

  3. Mohammed says

    I ave got this error when I click in add comment button:

    Illuminate\Database\QueryException thrown with message “SQLSTATE[HY000]: General error: 1364 Field ‘parent_id’ doesn’t have a default value (SQL: insert into `comments` (`content`, `user_id`, `commentable_type`, `commentable_id`, `updated_at`, `created_at`) values (nice, 1, App\post, 3, 2018-08-14 22:00:03, 2018-08-14 22:00:03))”

    1. houda says

      bonjour Mohammed,j trouve même problème. est ce que tu a trouvé une solution?

      1. houda says

        Hey, salope we don’t speak frensh here.
        Thank you

        1. arsh says

          Thanks a lot

          1. arsh says

            🙂

    2. Carlos says

      set default for parent_id to null in database

    3. Chris says

      How did you solve the error?

  4. a says

    magnificent points altogether, you simply won a new reader.
    What may you recommend about your post that you simply
    made a few days ago? Any certain?

  5. LARAVEL Development says

    Awesome Article, More appreciating blog! Great Explanation !
    Keep going!

  6. source says

    Have you ever thought about publishing an ebook or
    guest authoring on other sites? I have a blog based upon on the same subjects you discuss and would really like to
    have you share some stories/information. I know my viewers would enjoy your
    work. If you’re even remotely interested, feel free to send me
    an e-mail.

  7. mcpaul says

    Hey, thanks for awesome tutorial.
    Could you briefly explain the store function in the CommentController. It works fine, just wish to understand how the fields commentable_id and commentable_type get populated with their respective values.
    Thanks

  8. jambang says

    Awesome! Its truly amazing post, I have got much clear idea on the topic of from this post.

  9. Prajwal says

    Thank You, 🙂

  10. Elvis Jane says

    Hi, I do believe this is an excellent site.

  11. Ruben says

    Hi Krunal,

    Nice post! Maybe as an addition, I have added the following function in my model to recursively delete top-level comments.

    /**
    * Recursively delete the comment with replies all multiple levels
    * @throws Exception
    */
    public function deleteWithReplies()
    {
    if(count($this->replies) > 0) {
    // Delete children recursive
    foreach ($this->replies as $reply) {
    $reply->deleteWithReplies();
    }
    }
    $this->delete();
    }

    Best,

    Ruben

    1. github.com/dedyyyy says

      Work perfectly sir

    2. mie says

      can u give other instruction? this code just for controller ..how about route and code for blade.php?

  12. Robson says

    Nice!

    1. nice says

      Nice

  13. Undangan Nikah says

    I was suggested this blog via my cousin. I am now not certain whether or not this
    submit is written through him as nbody else recognise such
    distinctive about my trouble. You’re amazing!
    Thanks!

  14. Steve says

    Thank you for the excellent information you have shared with us, Please keep us update on new technologies

  15. Milon2222 says

    What a nice blog it is! Really an awesome Blog. Thank’s a lot for sharing.

  16. Jannat says

    Many many thank’s for sharing this attractive Blog with us. Thank’s a lot.

  17. github.com/dedyyyy says

    im building API version for this feature, Nice article sir, help me so much thankyou

  18. github.com/dedyyyy says

    Thank you, i am building API version of this

  19. steven says

    that was very good, thank you very much for this article!!!

  20. ilham says

    it’s very usefull.
    but how to limit depth of replies comment? example: 3 depth of replies comment.
    because my web looks ugly, when reply messageis too much.

  21. Vivek says

    Brilliant explanation and working in the first effort. Thanks

  22. yash says

    laravel

    1. sunny says

      This is not done

  23. asad says

    alert(‘Greate Article’)

  24. Penaline Chepkoech says

    Nice guide

  25. Penaline Chepkoech says

    Found it interesting

  26. jaspreet says

    nice tutorial thanks

  27. kodunmi lekan says

    i have issue of infinity loop

    This page isn’t working 127.0.0.1 is currently unable to handle this request.
    HTTP ERROR 500

    how can i solve it thanks

  28. Joseph says

    In the show.blade.php, It cannot load the comment. Olease help

    1. Joseph says

      This code is not loading the comments. Those who found it interesting what did you do

  29. Akcan says

    Hi, thanks for this awesome tutorial.
    I follow this steps and i came across n+1 problem when comments nested more than 2 level.

    is there any suggestions ?

  30. noobie says

    thanks

  31. Azriel Fatur says

    testing

  32. Inexture Solutions LLP says

    Amazing Post, Its so Interesting. Thanks for this Post.

  33. Surbhi says

    What is the use of commentable_type.?

  34. Virajee Amarasinghe says

    Nice Tutorial. Thanks 🙂

  35. Chris says

    Nice tutorial thank u.
    Do you have one for uploading of mp3 files by user that can be downloaded by visitors. Would be of great help.

  36. assad says

    so nice ! It is so easy and very clean like it .

  37. eYoga Club says

    Great post, I genuinely enjoyed reading it

  38. John says

    this article helped , thank you

  39. Jimmy says

    Create Post this errors

    1. Krunal says

      From the error, it seems like there is a mismatch in your route config and ahref link reference.

  40. Adrian Lopiccolo says

    the Unexpected:

  41. Wunna says

    Nice, thank a lot

  42. laravel development says

    Nice Blog ! Keep sharing

  43. laravel development says

    awesome ! Thanks for sharing. I appreciate.

  44. Ismail khan says

    Perfect, Appreciate it. You did a lot of work. Thanks a lot.

  45. humawan says

    Thank you for your article. I’ll try up this!

  46. Sebastian Samuel says

    Good job. I really enjoyed its reading this post enough to search for writing this excellent article and helpful post thanks for sharing.

Leave A Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.