Installation
composer require rostami/commentable
Publish the migration and config:
php artisan vendor:publish --provider="Rostami\Commentable\CommentableServiceProvider"
php artisan migrate
Usage on a Model
Add the Commentable trait to your Eloquent model:
use Rostami\Commentable\Commentable;
class Post extends Model
{
use Commentable;
}
First Comment Attach comments to a model instance:
$post = Post::find(1);
$post->comments()->create([
'user_id' => auth()->id(),
'body' => 'This is a great post!',
]);
Display Comments Retrieve comments via the relationship:
$comments = $post->comments()->with('user')->get();
config/commentable.php (for customization)app/Models/Comment.php (default comment model)database/migrations/ (schema for comments table)Commenting on Models
// Create a comment
$model->comments()->create($validatedData);
// Bulk creation (e.g., from a form)
$model->comments()->createMany($request->comments);
Nested Comments (Threads)
// Reply to a comment
$comment->replies()->create([
'user_id' => auth()->id(),
'body' => 'Thanks for your input!',
'parent_id' => $comment->id, // Explicitly set parent
]);
Querying Comments
// Get top-level comments (non-replies)
$model->comments()->whereNull('parent_id')->get();
// Get a comment thread (recursive)
$comment->load('replies.user');
Soft Deletes
// Delete a comment (soft delete)
$comment->delete();
// Restore a comment
$comment->restore();
max:1000, string).can('delete', $comment)).CommentCreated, CommentDeleted events for notifications.api_resources to structure comment responses:
$comment->load('user', 'replies.user');
return new CommentResource($comment);
Custom Comment Model
Override the default Comment model in config/commentable.php:
'model' => App\Models\CustomComment::class,
Additional Fields
Add fields to the comments table via migration, then update the Comment model’s $fillable.
Scopes
Add query scopes to the Comment model:
public function scopeApproved($query)
{
return $query->where('approved', true);
}
Missing Migration
If you forget to run php artisan migrate, comments will fail to save. Always check the comments table exists.
Parent-Child Relationships
parent_id when replying creates a top-level comment.reply() helper if provided:
$comment->reply($request->body);
Circular References
replies recursively can cause N+1 queries or stack overflows.withCount('replies') for metrics.Soft Deletes Confusion
delete() soft-deletes comments but may not update UI if not handled in frontend.Comment::withTrashed() to fetch soft-deleted comments.User Association
user_id is always set (e.g., for anonymous comments).user_id to the migration or handle null cases in logic.dd($model->comments()->getQuery()->toSql());
dd(config('commentable'));
php artisan tinker
$post = App\Post::first();
$post->comments()->create(['body' => 'Test']);
Bulk Operations
Avoid bulk creating comments in loops; use createMany() instead.
Caching Cache comment counts or threads if frequently accessed:
$commentCount = Cache::remember("comments_{$model->id}", now()->addHours(1), function() {
return $model->comments()->count();
});
Database Indexes
Add indexes to user_id, parent_id, and commentable_id for large datasets:
Schema::table('comments', function (Blueprint $table) {
$table->index('user_id');
$table->index('parent_id');
$table->index('commentable_id');
});
Custom Commentable Types
Extend the Commentable trait to support polymorphic relationships (e.g., comments on Post, Video):
class Commentable
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
Comment Moderation
Add approved column and use model observers:
class CommentObserver
{
public function saving(Comment $comment)
{
if (!$comment->approved && $comment->isDirty('approved')) {
// Trigger notification
}
}
}
Comment Notifications Use Laravel Notifications to alert users when replied to:
$comment->user->notify(new CommentReplyNotification($comment));
How can I help you explore Laravel packages today?