›
byrcsc/laravel-comments · 1.x
Add comments to an Eloquent model and use the relationships provided by HasComments.
Any Eloquent model becomes commentable by adding one trait. This page covers what that trait gives you and what a comment row holds.
use ByRcsc\LaravelComments\Concerns\HasComments;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasComments;
}There is no registration step and no interface to implement. The trait adds a polymorphic relation and two write methods.
comment() takes a body and the model writing it:
$comment = $post->comment('Great write-up!', by: $user);$by is any Eloquent model. The package stores commentator_type and
commentator_id as a morph and never assumes a user class.
commentAsGuest() takes a name and an email instead:
$comment = $post->commentAsGuest(
'Where can I download the slides?',
name: 'Jane',
email: 'jane@example.com',
);A comment carries exactly one of the two identities. A guest comment has a null
commentator_type, which is how the package tells them apart, and why guest
comments have their own default status, cannot react, and are never notified.
Both methods throw a CommentableNotPersistedException when the commentable
has not been saved yet. There is nothing for the comment to point at.
$post->comments; // every comment, any status, any depth
$post->comments()->count();The relation is a plain MorphMany, so everything you know about Eloquent
applies. Narrow it with the scopes:
$post->comments()->approved()->topLevel()->with('replies')->get();| Scope | Returns |
|---|---|
topLevel() | Thread starters, comments with no parent |
pending() | Comments waiting on a moderator |
approved() | Comments in the approved set |
rejected() | Comments a moderator turned down |
spam() | Comments marked as spam, kept apart from rejected |
pinned() | Comments held at the top of their thread |
pinnedFirst() | Ordering: pinned first, most recently pinned among them |
Soft-deleted comments are excluded by Eloquent's own global scope; reach them
with withTrashed(). See deleting comments.
| Column | Meaning |
|---|---|
commentable_type, commentable_id | The record being commented on |
commentator_type, commentator_id | Who wrote it, or null for a guest |
guest_name, guest_email | The guest identity, or null for a model-authored comment |
parent_id | The comment being replied to, or null at the top |
body | Stored verbatim, untrusted input |
status | pending, approved, rejected, or spam |
edited_at | When the body last changed, or null |
pinned_at | When it was pinned, or null |
reply_notified_at | When its author was told about it, or null |
deleted_at | Soft-delete tombstone marker |
status casts to a CommentStatus enum; the four timestamps cast to Carbon.
$comment->commentable; // the record it is on
$comment->commentator; // the model that wrote it, or null for a guest
$comment->parent; // the comment it replies to, or null
$comment->replies; // direct replies only
$comment->reactions; // every reaction row
$comment->reactionCounts; // grouped counts, for rendering
$comment->revisions; // prior bodies, oldest first
$comment->attachments; // attachment metadata, oldest first$comment->isBy($user); // boolBoth halves of the morph have to match: a User and an Admin that happen to
share a primary key are not the same author. A guest-authored comment matches
nobody, which is what makes ownership checks deny for one.
A commentable can keep a denormalized count of its approved, non-deleted comments on its own table. It is off until you override one method and add the column yourself:
public function commentsCountColumn(): ?string
{
return 'comments_count';
}See comment counts for the migration, the maintenance guarantees, and the repair command.
A commentable can implement DecidesCommentStatus to say what status its new
comments start in, overriding both configured defaults. See
initial status.