byrcsc/laravel-comments · 1.x
Threads and replies.
Build threads with reply(), read them with the relations, and understand the depth limit and what it does and does not reshape.
A thread is a comment and everything hanging off its parent_id. Replies are
ordinary comments with a parent, on the same commentable, carrying their own
status, reactions, revisions, and attachments.
Reply to a comment
$reply = $comment->reply('Agreed, especially the last section.', by: $teammate);
$guestReply = $comment->replyAsGuest(
'Same question here.',
name: 'Jane',
email: 'jane@example.com',
);Both mirror the write methods on the commentable, and both take the parent's
commentable_type and commentable_id automatically — a reply is always on
the same record as the comment it answers.
Replying to an unsaved comment throws a LogicException. Persist the parent
first.
Read a thread
Load thread starters with their replies, nesting to the depth your interface renders:
$threads = $post->comments()
->approved()
->topLevel()
->with('replies.replies')
->get();replies is direct replies only. There is no recursive relation and no
materialized path column: the package stores the parent key and leaves the
shape of the query to you, because a two-level interface and a fully nested one
want different things.
For a flat list — a moderation queue, an activity feed — skip topLevel()
entirely and read every comment at once.
Ask how deep a comment sits
$comment->depth(); // 0 at the top, one more per level aboveThe walk follows parent up the chain, so it costs a query per level unless
the parents are already loaded. It reads the chain rather than a stored column,
which means it cannot disagree with the table.
The depth limit
comments.max_depth caps how deep replies nest. It defaults to 3, so a
top-level comment sits at depth 0 and replies are allowed at depths 1, 2, and 3.
'max_depth' => 3, // null for unlimitedCreating a reply below the limit throws a ThreadTooDeepException and stores
nothing:
use ByRcsc\LaravelComments\Exceptions\ThreadTooDeepException;
try {
$deep->reply('One level too far', by: $user);
} catch (ThreadTooDeepException $e) {
// Nothing was written.
}Two things about the limit are worth knowing before you change it.
It applies at creation only. Tightening the limit never reshapes existing threads: comments already deeper than the new value stay exactly where they are, and stay readable. Only new replies are refused.
It applies to every write path. The check runs on Eloquent's creating
event, so factories and Comments::fake() are held to it as well as
reply(). A test cannot build a thread the engine would refuse.
The body length limit
comments.max_length is the other creation-time gate, in characters:
'max_length' => 2_000, // null for no limit beyond the columnExceeding it throws a BodyTooLongException and stores nothing. Multibyte
characters count as one, and nothing is ever silently truncated. The same check
runs again on an edit, so an edit cannot do what a write was refused — see
edits and revisions.
Threads and deletion
Soft deleting a comment leaves its replies readable under a tombstone: the
parent is hidden by Eloquent's global scope, the children are not. Force
deleting takes the whole subtree with it through the database's cascade on
parent_id.
That asymmetry is deliberate, and it has consequences for events and counts. See deleting comments.