Skip to content

Comments

Comments can attach to any model in Reshape — companies, announcements, customers, or your own models — and support unlimited nested replies. They're rich text (via Tiptap on the frontend), moderated through a status field, and reactable in their own right.

A threaded comment discussion

Attaching comments to a model

Any model can become commentable by implementing App\Contracts\CommentableInterface and using App\Concerns\HasComments. User, Company, Announcement, and Customer all do. To make your own model commentable, do the same — no migration changes needed, since comments are stored polymorphically.

Trait Methods (HasComments)

php
// The root (top-level) comments belonging to this model...
$model->comments() : Illuminate\Database\Eloquent\Relations\MorphMany

// Determine whether this model has any comments...
$model->hasComments() : bool

Actions (app/Actions/Comments/)

php
// Create a comment...
CreateComment::create(array $input) : App\Models\Comment

// Update a comment's body/status...
UpdateComment::update(Comment $comment, array $input) : void

// Soft-delete a comment by marking its status Deleted rather than removing the row...
DeleteComment::delete(Comment $comment) : void

Threading

Rather than a naive parent_id self-join, comments use staudenmeir/laravel-adjacency-list, which lets you query entire subtrees efficiently with recursive CTEs. Replies are limited to 10 levels deep by default (Comment::getDefaultMaxDepth()).

Fetching and shaping a thread into a tree (rather than a flat list) goes through App\Services\CommentTreeBuilder:

php
// Build a hierarchical tree from a flat collection of comments...
CommentTreeBuilder::tree(Collection $comments, string $mode = 'descendants') : Collection

$mode is one of descendants (a normal reply tree), siblings, or ancestors (e.g. "show me the thread around this comment").

Moderation

CommentStatus (Active, Pending, Inactive, FlaggedInactive, Deleted) drives a status-aware default ordering — comments awaiting review or restricted sort after normal active comments, generated dynamically as a SQL CASE expression (Comment::getDefaultCommentOrdering()) so status-based sorting works consistently across SQLite, MySQL, and PostgreSQL.

No @mentions

Comments support rich body content and an open meta array, but there's no built-in @mention parsing or mention notifications — if you need that, it's a natural extension point on top of the existing meta field.

In the frontend

web/src/components/comments/*comments-card.vue (paginated container), comment-form.vue (Tiptap editor), and comment-list.vue/comment-list-item.vue (recursive rendering, indentation driven by a depth.ts helper).