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. For example, multiple comments belong to multiple users, and a comment can have multiple replies.
Here are the steps to create comment nesting in Laravel:
Step 1: Install and configure Laravel.
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
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(); }); }
Migrate the database using the following cmd.
php artisan migrate
Step 3: Define Polymorphic Relationships.
We need to define the Polymorphic relationships between the models. So write the following code inside the 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 the 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’s 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 } }
We need to create a form for creating the post. So create a blade file inside the 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
Go to the resources >> views >> layouts >> app.blade.php file and add a link to create a post.
We must 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.
@else <li class="nav-item"> <a class="nav-link" href="{{ route('post.create') }}">Create Post</a> </li>
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 visit our form with the title and body form fields.
Step 4: Save and display the Post.
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
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
Now you can see the individual posts. Fine till now.
The next step is to display the comments on this post.
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 must create a form inside a show.blade.php file to add comments to a 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
We have added a form that can add comments. Now, we need to define the route to store the comment.
// web.php Route::post('/comment/store', 'CommentController@store')->name('comment.add');
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(); } }
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.
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.
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
Add the comment, which will show us here in the same url.
Step 7: Create a Reply form and save replies.
We must 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'); } }
We must add a primary key as a parent_id in the replies function to fetch a reply based on a parent comment’s id.
Okay, now we must display all the comments and reply codes in 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 the 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.
- comments
- post_id.
So, when we include this partial inside the show.blade.php file, we must pass both 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');
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 the 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(); } }
Almost both the function store and replyStore functions are the same here. 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.
- Post comments.
- Post id
We can add the parent comment from here but add the partials’ replies.
I have added the parent comment and its replies; our database table looks like this.
Also, our final output looks like below.
That’s it.

Krunal Lathiya is a seasoned Computer Science expert with over eight years in the tech industry. He boasts deep knowledge in Data Science and Machine Learning. Versed in Python, JavaScript, PHP, R, and Golang. Skilled in frameworks like Angular and React and platforms such as Node.js. His expertise spans both front-end and back-end development. His proficiency in the Python language stands as a testament to his versatility and commitment to the craft.
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.
yes this site is very interesting bro
I learn many thing from this site
this is very good this site very helpfull for me
â
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!
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))”
bonjour Mohammed,j trouve même problème. est ce que tu a trouvé une solution?
Hey, salope we don’t speak frensh here.
Thank you
Thanks a lot
🙂
set default for parent_id to null in database
How did you solve the error?
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?
Awesome Article, More appreciating blog! Great Explanation !
Keep going!
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.
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
Awesome! Its truly amazing post, I have got much clear idea on the topic of from this post.
Thank You, 🙂
Hi, I do believe this is an excellent site.
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
Work perfectly sir
can u give other instruction? this code just for controller ..how about route and code for blade.php?
can i look the repo
Nice!
Nice
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!
Thank you for the excellent information you have shared with us, Please keep us update on new technologies
What a nice blog it is! Really an awesome Blog. Thank’s a lot for sharing.
Many many thank’s for sharing this attractive Blog with us. Thank’s a lot.
im building API version for this feature, Nice article sir, help me so much thankyou
Thank you, i am building API version of this
that was very good, thank you very much for this article!!!
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.
Brilliant explanation and working in the first effort. Thanks
laravel
This is not done
alert(‘Greate Article’)
Nice guide
Found it interesting
nice tutorial thanks
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
In the show.blade.php, It cannot load the comment. Olease help
This code is not loading the comments. Those who found it interesting what did you do
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 ?
thanks
testing
Amazing Post, Its so Interesting. Thanks for this Post.
What is the use of commentable_type.?
Nice Tutorial. Thanks 🙂
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.
so nice ! It is so easy and very clean like it .
Great post, I genuinely enjoyed reading it
this article helped , thank you
Create Post this errors
From the error, it seems like there is a mismatch in your route config and ahref link reference.
the Unexpected:
Nice, thank a lot
Nice Blog ! Keep sharing
this is very good this site very helpfull for me
awesome ! Thanks for sharing. I appreciate.
Perfect, Appreciate it. You did a lot of work. Thanks a lot.
Thank you for your article. I’ll try up this!
Good job. I really enjoyed its reading this post enough to search for writing this excellent article and helpful post thanks for sharing.
Good job. I really enjoyed its reading this post enough to search for writing this excellent article and helpful post thanks for sharing.
this is very good this site very helpfull for me
I am using API version for this function, It’ll help me a lot.
Thanks for sharing the useful one!
Nice Blog
Love Your content
I want to show my affection for your generosity for men who should have help on that topic. Your personal dedication to passing the solution all through had been astonishingly advantageous and have consistently encouraged employees just like me to achieve their desired goals. Your warm and helpful help denotes so much a person like me and even more to my mates. Thank you; from everyone of us.
This article is creative and informative. I actually look such website for our company.
Thanks for sharing such a nice and informative article
Regards