Associate comments with any Eloquent model. Comments can be nested (replies), reacted to with emoji, rendered from Markdown to sanitized HTML, held for moderation, mention other users, and notify people who subscribed to a model's comments.
Install the package via composer:
composer require lenorix/laravel-commentsPublish and run the migrations:
php artisan vendor:publish --tag="comments-migrations"
php artisan migratePublish the config file:
php artisan vendor:publish --tag="comments-config"Add the HasComments trait to any model that should receive comments, and implement
commentableName() / commentUrl() β notifications use them to describe where a
comment was posted:
use Illuminate\Database\Eloquent\Model;
use Lenorix\LaravelComments\Models\Concerns\HasComments;
use Lenorix\LaravelComments\Models\Concerns\Interfaces\Commentable;
class Post extends Model implements Commentable
{
use HasComments;
public function commentableName(): string
{
return $this->title;
}
public function commentUrl(): string
{
return route('posts.show', $this);
}
}If comments have an author (most apps do), prepare your user model and point the config at it:
use Illuminate\Foundation\Auth\User as Authenticatable;
use Lenorix\LaravelComments\Models\Concerns\InteractsWithComments;
use Lenorix\LaravelComments\Models\Concerns\Interfaces\CanComment;
class User extends Authenticatable implements CanComment
{
use InteractsWithComments;
}// config/comments.php
'models' => [
'commentator' => App\Models\User::class,
],If you want to allow comments from guests (no logged-in user), set:
// config/comments.php
'allow_anonymous_comments' => true,$post->comment('Great read!'); // as the current authenticated user
$post->comment('Great read!', $anotherUser); // on behalf of a specific user
$post->comments; // all comments, replies included
$post->comments()->topLevel()->get(); // only root comments, no repliesA Comment can itself receive comments, which makes it a reply:
$comment = $post->comment('Great read!');
$reply = $comment->comment('I agree!');
$comment->comments; // replies to this specific commentDeleting a comment leaves its replies in place by default. Set
delete_replies_along_comments to true in the config to delete them too, at every
depth of nesting.
$comment->react('π');
$comment->react('π', $anotherUser);
$comment->deleteReaction('π');
$comment->reactions->summary();
// [['reaction' => 'π', 'count' => 3, 'commentator_reacted' => true], ...]Reacting always requires an identified commentator, even if allow_anonymous_comments
is true β react() throws AnonymousReactionsNotAllowed when no commentator can be
resolved. Guests can comment, but never react.
Only emoji listed in allowed_reactions are accepted β react() throws
DisallowedReaction otherwise. Set allowed_reactions to an empty array to allow any
reaction.
By default, original_text is rendered from Markdown into HTML and stored in text.
Any HTML tag or attribute not on the sanitizer's whitelist is stripped. Extend that
whitelist per tag in the config:
// config/comments.php
'allowed_attributes' => [
'p' => ['data-test'],
],Use text to display a comment, and original_text when editing it.
Syntax highlighting for code blocks is not bundled. Add it by writing a
CommentTransformer that runs after MarkdownToHtmlTransformer in
comment_transformers and rewrites $comment->text with whichever highlighter your
app already depends on.
// config/comments.php
'automatically_approve_all_comments' => false,$comment->isApproved();
$comment->isPending();
$comment->approve();
$comment->reject(); // deletes the comment
Comment::approved()->get();
Comment::pending()->get();Register who can approve pending comments, and expose the signed approve/reject routes:
// in a service provider
PendingCommentNotification::sendTo(fn (Comment $comment) => User::where('is_admin', true)->get());// in a routes file
Route::comments();Override shouldBeAutomaticallyApproved() on a custom Comment subclass for
fine-grained approval rules instead of the global config flag.
use Lenorix\LaravelComments\Enums\NotificationSubscriptionType;
$user->subscribeToCommentNotifications($post, NotificationSubscriptionType::All);
$user->subscribeToCommentNotifications($post, NotificationSubscriptionType::Participating);
$user->unsubscribeFromCommentNotifications($post);
$user->unsubscribeFromAllCommentNotifications();All subscribers hear about every new approved comment on the model. Participating
subscribers only hear about it if they have commented on that model themselves. The
author of a comment is never notified about their own comment.
Customize the sender and the mail's content:
// config/comments.php
'notifications' => [
'mail' => [
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
],
],php artisan vendor:publish --tag="comments-views"This publishes editable Blade templates to resources/views/vendor/comments/mail/.
The pending-comment mail includes working approve/reject buttons.
Mentions are represented as <span data-mention="{id}">{name}</span> inside the
rendered text. Enable them and pick a raw-input convention (the default recognizes
@[Name](id)):
// config/comments.php
'mentions' => [
'enabled' => true,
],$comment->mentionedCommentators(); // commentator models mentioned in this commentMentioned commentators are notified once the comment is approved, not while pending.
The package ships a CommentPolicy, automatically bound to your configured Comment
model. Extend it to customize create, update, delete, react, see, approve
and reject rules:
// config/comments.php
'policies' => [
'comment' => App\Policies\CustomCommentPolicy::class,
],CommentCreated and CommentDeleted are dispatched from the Comment model's own
created/deleted lifecycle, so they fire no matter how the row came to exist or
disappear β a top-level comment, a reply, direct model calls, or the
delete_replies_along_comments cascade all dispatch them:
use Lenorix\LaravelComments\Events\CommentCreated;
use Lenorix\LaravelComments\Events\CommentDeleted;
Event::listen(function (CommentCreated $event) {
// $event->comment
});CommentCreated fires even for a pending comment β check $event->comment->isApproved()
if you only care about visible ones.
composer testPlease see CHANGELOG for more information on what has changed recently.
Please review our security policy on how to report security vulnerabilities.
Released into the public domain under The Unlicense.