Reading & counting comments
Relations
$post->comments; // every comment written on the post
$post->approvedComments; // visible + approved top-level comments, ordered per config
$user->writtenComments; // every comment authored by the user
$comment->actor; // the model that wrote the comment (null for anonymous)
$comment->commentable; // the model the comment was written onThe fluent query
Comments::query() returns a site-wide CommentQuery; Comments::for($subject) and Comments::byAuthor($author) return one already scoped — a read side that mirrors the write builder. Chain filters and ordering, then fetch:
use RoundlyConsulting\Comments\Facades\Comments;
Comments::for($post)->approved()->newest()->paginate(20);
Comments::for($post)->visible()->rootsOnly()->withReplies()->get(); // public replies only
Comments::for($post)->pending()->count();
Comments::byAuthor($user)->get();
Comments::query()->hidden()->newest()->get(); // site-wide, every subjectwithReplies() follows the query’s visibility: on a visible() query (in either order) only visible + approved replies load, at every level; without visible() every reply loads, for a moderation view.
| Method | Purpose |
|---|---|
for($subject) / byAuthor($author) | Scope the query — they also chain onto Comments::query(). |
approved() / pending() / hidden() | Filter by moderation status. |
visible() | Visible flag on AND status approved. |
rootsOnly() | Top-level comments only. |
withReplies() | Eager-load nested replies up to max_depth — only visible + approved ones on a visible() query, every reply otherwise. |
newest() / oldest() | Order by created_at. |
orderByLikesDesc() / orderByTrending() | Rank by likes — see Likes & ranking. |
mostReported() / reportedMoreThan() / withReportCounts() | Moderation queues — see Reporting & auto-moderation. |
get() / paginate($perPage = 15) / count() | Fetch, paginate or count the matches. |
approveAll() / hideAll() / deleteAll() | Bulk moderation — see Moderation. |
tap($callback) / query() | Refine or take the underlying Eloquent builder. |
Escape hatches
When the built-in filters aren’t enough, tap() hands you the underlying Eloquent builder and query() returns it:
Comments::for($post)
->visible()
->tap(fn ($query) => $query->where('created_at', '>=', now()->subWeek()))
->get();
$builder = Comments::for($post)->approved()->query(); // the underlying Builder<Comment>Comment counts
Show “X comments” on list pages without counting per row. Both helpers set a comments_count attribute:
$post->loadCommentCount(); // sets $post->comments_count
Post::withCommentCounts()->get(); // adds comments_count without N+1comments_count is what the public sees: every visible + approved comment on the subject, replies included — the same rows as Comment::visible(). Pending, hidden and visible(false) comments are never counted.
Comment attributes
$comment->comment; // the body (stored in the `comment` column)
$comment->status; // CommentStatus enum
$comment->visible; // bool
$comment->parent_id; // ?int — null for top-level comments
$comment->locked_at; // ?Carbon — set when the thread under this comment is locked
$comment->mentions; // CommentMention rowsShow your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.