Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Commentions Laravel Package

kirschbaum-development/commentions

Commentions is a Laravel package for adding comments and reactions to Eloquent models. Simple traits make models commentable and users commenters, with support for mentions, emoji reactions, and easy customization for displaying and managing comments in your app.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish in one pass.

composer require kirschbaum-development/commentions
php artisan vendor:publish --tag="commentions-migrations"
php artisan vendor:publish --tag="commentions-config"
php artisan migrate

Unlike many packages, the migrations are published, not auto-loaded. If comments silently don't work, this is almost always the cause.

Make a model commentable. Two steps, both required — the trait alone does nothing useful:

use Kirschbaum\Commentions\Contracts\Commentable;
use Kirschbaum\Commentions\Concerns\HasComments;

class Order extends Model implements Commentable
{
    use HasComments;
}

First use case: an internal notes panel on a resource. Add the form component to a resource form (typically a dedicated "Comments" tab or section) or render the Livewire component directly on a custom page:

use Kirschbaum\Commentions\Filament\Forms\Components\Comments;

public static function form(Form $form): Form
{
    return $form->schema([
        // ...
        Comments::make()->columnSpanFull(),
    ]);
}

For a standalone page or Blade view:

@livewire('commentions::comments', ['record' => $record])

Where to look first: the published config/commentions.php is the real map of the package — it tells you which model is the "commenter", how mentionables are resolved, which reactions are offered, and what the polling behavior is. Read it before reading the source. Then open Kirschbaum\Commentions\Comment to see the schema you'll be querying against.

The canonical first win is an Order or Ticket resource where support staff leave threaded notes with @-mentions of teammates. You get persistence, mention resolution, and emoji reactions without writing a single migration or Livewire component.

Implementation Patterns

Treat it as an inbox, not a form field. The form component is convenient, but the higher-leverage pattern is a table action or a slide-over on the resource's view page, so commenters never leave the list view:

use Kirschbaum\Commentions\Filament\Actions\CommentsAction;

Tables\Actions\Action::make('comments')
    ->label(fn ($record) => $record->comments()->count() . ' comments')
    ->modalContent(fn ($record) => view('comments-modal', ['record' => $record]));

If a dedicated action class isn't available in your Filament version, wire the Livewire component into a modal body — same result, one line of Blade.

Mentions are a resolver problem, not a UI problem. The @ autocomplete can only offer what your config exposes. For anything beyond "all users in one tenant," point the mentionable resolver at a scoped query:

// config/commentions.php
'mentionables' => [
    App\Models\User::class => [
        'name' => 'name',
        'avatar' => 'avatar_url',
    ],
],

Then scope it per-request with a service provider binding or an AppServiceProvider::boot() closure if you need tenant isolation. Do this before launch — retrofitting scoping after users have mentioned people across tenants is painful, because mention rows already exist.

Notifications belong in a listener, not in the component. The package records mentions; it doesn't decide who gets emailed. Hook the mention event (or poll comment->mentions) and dispatch your own notification:

Event::listen(CommentCreated::class, function ($event) {
    $event->comment->mentions->each(
        fn ($user) => $user->notify(new MentionedInComment($event->comment))
    );
});

This keeps delivery, digesting, and quiet hours in your app where they belong.

Reactions as lightweight triage. With a constrained emoji set ('reactions' => ['👍', '👀', '✅'] in config), reactions become a poor-man's status field — "acknowledged" vs. "resolved" — without a state machine. Keep the set small and intentional; users will read meaning into whatever you offer.

Polling off, refresh on demand. Since 1.0.4 you can force the comments list to refresh instead of relying on the polling interval. The pattern that scales best: disable polling globally (or per component), then trigger a refresh at the moments that actually matter — after posting a comment, when a slide-over/modal opens, or on an explicit "new comments" affordance. This keeps idle index pages cheap while still feeling live inside the thread.

Testing. Comments are ordinary Eloquent models, so standard factories and assertDatabaseHas work. Test the authorization path explicitly: the package does not know your policies, so anyone who can reach the component can comment unless you gate it. Add the check yourself:

Comments::make()
    ->visible(fn ($record) => auth()->user()->can('comment', $record));

Or gate at the policy level with a comment ability on the commentable model.

Multi-model commentability. Because Commentable is a morph, one Comment table serves Order, Invoice, and Project at once. Build a single reusable CommentsPanel Blade component or Filament partial and reuse it across resources rather than copy-pasting schema definitions.

Gotchas and Tips

  • Interface + trait, always together. HasComments without implements Commentable (or vice versa) yields confusing errors — usually an empty comment list or a RelationNotFoundException on the morph.
  • Published migrations, not loaded. Covered above, but worth repeating: a fresh clone on a new machine will fail unless php artisan migrate runs after publishing. Commit the published migration files.
  • Morph maps bite. If your app uses Relation::enforceMorphMap(), the commentable_type values stored by Commentions must match your aliases. Mismatches surface as comments vanishing after a morph map is introduced — and old rows don't get rewritten.
  • Rich text = XSS surface. Comments are rendered as HTML. Confirm how the body is sanitized before letting non-admin users comment; if you allow raw HTML through, add a sanitizer (e.g. mews/purifier) at render time rather than trusting the editor.
  • Commenter resolution depends on the auth guard. If you have separate web and admin guards, or run queue jobs/impersonation, the "current commenter" can resolve to the wrong (or null) user. Check the config's commenter resolver and, if needed, bind it explicitly per guard.
  • Polling is a quiet performance tax — and now optional. Live-updating comment threads re-query on an interval. On index pages with many rows, disable polling and refresh only inside the modal (1.0.4 adds a way to force a refresh of the comments list, so you can drop the interval entirely without losing the "just posted" update). Also eager-load comments counts in the table query to avoid N+1 on the badge column:
->withCount('comments')
  • Reactions need a uniqueness guard. Users will double-click. Verify the package's unique constraint on (comment, reactor, emoji) or add one; otherwise you get duplicate reaction rows and counts that drift.
  • Cascade deletes are your job. Deleting an Order will orphan its comments unless the morph is handled. Add a deleting hook on the commentable model, or a DB-level cleanup, or the table quietly grows forever.
  • Filament major-version coupling. The form component and action APIs track Filament's internals. Pin the package alongside your Filament version and check the release notes on upgrade — a Filament major bump is a breaking change here even if Commentions' own semver says otherwise. Release cadence is patch-level and additive so far (1.0.4 is a single feature on top of 1.0.3), so staying current within a minor is low-risk.
  • Extension points are thin by design. No built-in plugin/hook system: to change rendering, publish the views (--tag="commentions-views", if available) rather than subclassing components. To change behavior, extend Kirschbaum\Commentions\Comment and rebind it in a service provider — verify the package resolves it from the container before relying on that.
  • Small ecosystem, so budget for reading source. ~150 stars and no dependents means few StackOverflow answers. Everything you need is in src/; when behavior surprises you, read the Livewire component first — that's where the query, mention parsing, and authorization assumptions actually live. The 1.0.4 refresh hook lives there too, so it's the first place to look if the list doesn't update when you expect it to.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource