Skip to content

Mohcin Bounouara

Thoughts about software engineering and life

Building a reply system for comments in Laravel -Without a Subcomments Table-

render-self-reference-relationship-laravel
render-self-reference-relationship-laravel

Many developers, when adding a “reply to comment” feature in a Laravel app, often consider creating a separate sub_comments table (I was one of them, based on my lack of experience, I know this later). It sounds intuitive, because a reply is different from a comment, right? logic thinking

But there’s a cleaner, more strict way: self-referencing relationships in the samecomments table.

The Idea: one table to rule them all (Comments and Replies)

Instead of a separate sub_comments table, we just add a parent_id column to the existing comments table. This column references another comment by id in the same table, the one being replied to.

This technique is called a self-referencing relationship, and Laravel supports it in a clean way.

1- Update the comments table migration

In your migration, modify the comments table to include a parent_id column:

Schema::table('comments', function (Blueprint $table) {
    $table->unsignedBigInteger('parent_id')->nullable()->after('id');
    $table->foreign('parent_id')->references('id')->on('comments')->onDelete('cascade');
});

parent_id is nullable because top-level comments won’t have a parent.

The foreign key points back to the id of the same comments table.

2- Define relationships in the comment model

In your Comment model, add:

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

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

This gives you these collections:

  • $comment->parent : the comment this is replying to.
  • $comment->replies : all comments replying to this one.

3- You can fetch comments with replies in this way;

$comments = Comment::where('post_id', $post->id)
                   ->whereNull('parent_id')
                   ->with('replies')
                   ->get();

With condition:

4- You can display nested replies in blade template;

@foreach ($comments as $comment)
    <div class="comment">
        <p>{{ $comment->body }}</p>
                    @foreach ($comment->replies() as $childComment)
                        <div class="pl-8 bg-gray-200 p-4 rounded">
                            <p class="font-bold">{{ $childComment->name }}</p>
                            <p>{{ $childComment->content }}</p>
                        </div>
                    @endforeach
    </div>
@endforeach

Final notes:

  • No need for an extra table.
  • Easy to extend to unlimited nested replies.
  • Cleaner and more maintainable Eloquent relationships.
  • Works well with eager loading (with()).