PHPackages                             lenorix/laravel-comments - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. lenorix/laravel-comments

ActiveLibrary

lenorix/laravel-comments
========================

Associate comments with Eloquent models

v0.2.0(today)07↑2900%UnlicensePHPPHP ^8.3CI passing

Since Jul 31Pushed todayCompare

[ Source](https://github.com/lenorix/laravel-comments)[ Packagist](https://packagist.org/packages/lenorix/laravel-comments)[ Docs](https://github.com/lenorix/laravel-comments)[ GitHub Sponsors](https://github.com/lenorix)[ RSS](/packages/lenorix-laravel-comments/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (13)Versions (3)Used By (0)

Laravel Comments
================

[](#laravel-comments)

[![Latest Version on Packagist](https://camo.githubusercontent.com/3ccd098bf7a5c98de1d4352c9d3dfecd4d36b611ebebb93c066597044eb80af5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6c656e6f7269782f6c61726176656c2d636f6d6d656e74732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/lenorix/laravel-comments)[![GitHub Tests Action Status](https://github.com/lenorix/laravel-comments/actions/workflows/run-tests.yml/badge.svg)](https://github.com/lenorix/laravel-comments/actions?query=workflow%3Arun-tests+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/6a76cdb35285c50ec9edaf49e6d2524581ce384a4440568b669034a48c38b121/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6c656e6f7269782f6c61726176656c2d636f6d6d656e74732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/lenorix/laravel-comments)

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.

Installation
------------

[](#installation)

Install the package via composer:

```
composer require lenorix/laravel-comments
```

Publish and run the migrations:

```
php artisan vendor:publish --tag="comments-migrations"
php artisan migrate
```

Publish the config file:

```
php artisan vendor:publish --tag="comments-config"
```

Setup
-----

[](#setup)

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,
```

Usage
-----

[](#usage)

### Creating and reading comments

[](#creating-and-reading-comments)

```
$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 replies
```

### Replies

[](#replies)

A `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 comment
```

Deleting 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.

### Reactions

[](#reactions)

```
$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.

### Markdown rendering and sanitizing

[](#markdown-rendering-and-sanitizing)

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.

### Moderation

[](#moderation)

```
// 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.

### Notifications and subscriptions

[](#notifications-and-subscriptions)

```
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

[](#mentions)

Mentions are represented as `{name}` 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 comment
```

Mentioned commentators are notified once the comment is approved, not while pending.

### Authorization

[](#authorization)

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,
],
```

### Events

[](#events)

`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.

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [lenorix](https://github.com/lenorix)
- [All Contributors](../../contributors)

License
-------

[](#license)

Released into the public domain under [The Unlicense](LICENSE.md).

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance100

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/180887752?v=4)[lenorix](/maintainers/lenorix)[@lenorix](https://github.com/lenorix)

---

Top Contributors

[![jhg](https://avatars.githubusercontent.com/u/1288711?v=4)](https://github.com/jhg "jhg (17 commits)")

---

Tags

laravellaravel-commentslenorix

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/lenorix-laravel-comments/health.svg)

```
[![Health](https://phpackages.com/badges/lenorix-laravel-comments/health.svg)](https://phpackages.com/packages/lenorix-laravel-comments)
```

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k107.5M1.5k](/packages/spatie-laravel-permission)[dedoc/scramble

Automatic generation of API documentation for Laravel applications.

2.2k12.6M120](/packages/dedoc-scramble)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k5.4M49](/packages/spatie-laravel-pdf)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

329575.9k33](/packages/codewithdennis-filament-select-tree)[guava/filament-knowledge-base

A filament plugin that adds a knowledge base and help to your filament panel(s).

210164.0k3](/packages/guava-filament-knowledge-base)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

24773.9k](/packages/harris21-laravel-fuse)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
