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.
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.
// 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
// 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
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").
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.
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.
Attaching comments to a model
Any model can become commentable by implementing
App\Contracts\CommentableInterfaceand usingApp\Concerns\HasComments.User,Company,Announcement, andCustomerall do. To make your own model commentable, do the same — no migration changes needed, since comments are stored polymorphically.Trait Methods (HasComments)
Actions (
app/Actions/Comments/) Threading
Rather than a naive
parent_idself-join, comments usestaudenmeir/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:$modeis one ofdescendants(a normal reply tree),siblings, orancestors(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 SQLCASEexpression (Comment::getDefaultCommentOrdering()) so status-based sorting works consistently across SQLite, MySQL, and PostgreSQL.No @mentions
Comments support rich
bodycontent and an openmetaarray, but there's no built-in@mentionparsing or mention notifications — if you need that, it's a natural extension point on top of the existingmetafield.In the frontend
web/src/components/comments/*—comments-card.vue(paginated container),comment-form.vue(Tiptap editor), andcomment-list.vue/comment-list-item.vue(recursive rendering, indentation driven by adepth.tshelper).