Search by

x-laravel / commentable

X-Adam

Comments for Eloquent models with nested replies, anonymous commenters and cleanup of orphaned comments.

Package info

github.com/x-laravel/commentable

pkg:composer/x-laravel/commentable

Statistics

Installs: 13

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.0.0 2026-10-19 17:37 UTC

This package is auto-updated.

Last update: 2026-10-19 17:42:28 UTC


README

Tests Latest Stable Version PHP Version Total Downloads License

Polymorphic, infinitely nested comments for Eloquent. Any model can receive comments, any model can post them, and commenters are optional, so anonymous comments work too. Replies are loaded only when asked for.

Requirements

Package version PHP Laravel
1.x 8.4+ 13

Installation

composer require x-laravel/commentable
php artisan vendor:publish --tag=commentable-migrations
php artisan migrate

Publish the config to change the defaults:

php artisan vendor:publish --tag=commentable-config

Preparing the Models

Implement CanComment and use Commenter on the model that posts comments. Implement HasComments and use Commentable on every model that can receive them; only those models are accepted:

use XLaravel\Commentable\Concerns\Commentable;
use XLaravel\Commentable\Concerns\Commenter;
use XLaravel\Commentable\Contracts\CanComment;
use XLaravel\Commentable\Contracts\HasComments;

class User extends Authenticatable implements CanComment
{
    use Commenter;
}

class Post extends Model implements HasComments
{
    use Commentable;
}

The commentable_id and commenter_id columns follow Laravel's morph key type, so set Schema::defaultMorphKeyType('uuid') or 'ulid' before migrating if your models use them.

Configuration

Key Default Meaning
connection null Connection of the comment table
comment.table 'comments' Name of the comment table
comment.model Comment::class Comment model; must extend the package's
prune.enabled true Schedule the pruning of left-behind comments
prune.schedule 'daily' A cron expression, or a frequency method with its arguments: 'dailyAt:03:30', 'twiceDaily:1,13', 'weeklyOn:1,04:00'
prune.chunk 1000 Rows deleted by each pruning query

Commenting

$post->addComment('Great post!');                  // anonymous
$post->addComment('Great post!', $user);
$user->comment($post, 'Great post!');

$post->addReply($comment, 'I agree!', $user);
$user->reply($post, $comment, 'I agree!');

Replies can be added to any comment or reply, with no depth limit. A parent comment that belongs to another model is refused with InvalidArgumentException.

Listing

$post->rootComments()->withCount('replies')->with('commenter')->paginate(20);
$comment->replies()->withCount('replies')->with('commenter')->paginate(10);

$post->comments;         // every comment on the post, replies included
$user->authoredComments; // every comment the user posted

$comment->parent;
$comment->commentable;
$comment->commenter;
$comment->isReply();
$comment->isOn($post);

The relations load nothing extra by default; chain counts and relations as each screen needs them.

Comment keys are ULIDs, so they can appear in URLs without revealing how many comments exist.

Deleting and Pruning

$comment->delete();       // soft delete
$comment->restore();
$comment->forceDelete();  // deletes the replies with it through the foreign key

Soft-deleting a comment leaves its replies in place. Add a deleting listener in your application if they should go with it.

When a commented model or a commenter is deleted, its comments are permanently deleted with it, soft-deleted ones included, and the foreign key takes their replies along, whoever wrote them. Soft-deleted models keep their comments until they are force deleted.

Deletes that skip model events, such as bulk queries, database cascades or raw SQL, leave comments behind. The package schedules model:prune, daily by default, to remove comments whose commented model or commenter no longer exists. Anonymous comments are kept. Run it by hand with:

php artisan model:prune --model="XLaravel\Commentable\Models\Comment"

Pruning skips types that no longer resolve to a model and models on another connection.

Reacting to Comments

XLaravel\Commentable\Events\Commented is dispatched for a new comment and Replied for a new reply; $event->comment->parent is the comment that was replied to. CommentDeleted is dispatched when a comment is soft or force deleted. All three carry the Comment and are dispatched after the surrounding transaction commits.

Comments removed together with their model or commenter, by the foreign key or by pruning dispatch nothing.

Testing

composer install
composer test
composer analyse

License

Open source software licensed under the MIT license.