›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-comments
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Commentable models
  • Threads and replies
  • Moderation
  • Initial status
  • Pinning
  • Reactions
  • Edits and revisions
  • Attachments
  • Deleting comments

Operations

  • Comment counts
  • Events and listeners
  • Reply notifications
  • Authorization
  • Rendering and safety

Reference

  • Configuration
  • Console commands
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Commentable models
  • Threads and replies
  • Moderation
  • Initial status
  • Pinning
  • Reactions
  • Edits and revisions
  • Attachments
  • Deleting comments

Operations

  • Comment counts
  • Events and listeners
  • Reply notifications
  • Authorization
  • Rendering and safety

Reference

  • Configuration
  • Console commands
  • Testing
  • Troubleshooting

byrcsc/laravel-comments · 1.x

Events and listeners.

React to comment, moderation, reaction, attachment, and pin changes.

Every event carries the comment it happened to, so a listener that only cares which record moved can type-hint the base class CommentEvent and be done. Everything else, the thread, the commentable, the commentator, is derivable from the comment.

Events fire whether or not any of the package's own features are switched on. Your listeners are never coupled to its config.

The full list

Lifecycle

EventFires when
CommentCreatedA comment or reply is written, guest or not
CommentUpdatedAny saved change: a body edit, a transition, a pin, a restore
CommentDeletedA soft delete, and also a force delete
CommentRestoredA tombstone is restored
CommentForceDeletedA comment is removed for good

CommentForceDeleted additionally carries $countableRemoved: how many comments in the removed subtree were approved and not soft deleted. See deleting comments.

Moderation, base CommentModerated

EventFires when
CommentApprovedapprove() moved it
CommentRejectedreject() moved it
CommentMarkedAsSpammarkAsSpam() moved it

Each carries $previousStatus and $actor (null when nobody was named).

Reactions, base CommentReacted

EventFires when
ReactionAddedA reaction row actually appeared
ReactionRemovedOne actually went away

Each carries $reactor and $reaction.

Attachments, base CommentAttachmentChanged

EventFires when
AttachmentAddedA row is recorded
AttachmentRemoveddetach() removes one, or a force delete takes one

Each carries $attachment.

Pinning, base CommentPinChanged

EventFires when
CommentPinnedA pin actually went up
CommentUnpinnedOne actually came down

Each carries $actor.

Rule one: listen by concrete class

Laravel's dispatcher resolves listeners by interface but never by parent class. Listening to CommentModerated alone would never fire, name the concrete events and type-hint the base:

use ByRcsc\LaravelComments\Events\CommentApproved;
use ByRcsc\LaravelComments\Events\CommentMarkedAsSpam;
use ByRcsc\LaravelComments\Events\CommentModerated;
use ByRcsc\LaravelComments\Events\CommentRejected;
use Illuminate\Support\Facades\Event;

Event::listen(
    [CommentApproved::class, CommentRejected::class, CommentMarkedAsSpam::class],
    fn (CommentModerated $event) => $this->reindex($event->comment),
);

The same applies to CommentReacted, CommentAttachmentChanged, CommentPinChanged, and CommentEvent: useful as type hints, never as subscriptions.

Rule two: one event means one real change

Re-entering a status writes nothing and fires nothing. Pinning a pinned comment does the same. Reacting twice with the same reaction hands back the existing row and fires nothing. Detaching an attachment that is already gone fires nothing.

That is a guarantee you can build on: a listener counting approvals counts real ones, and a notification cannot double up under a double click.

The events also fire only once the write is known to have landed. A host saving listener that halts the save leaves nothing counting a state change the table never took.

Queue anything slow

Events fire inside whatever transaction caused them, synchronously. A listener that does anything slow or external should be queued, and a queued listener should dispatch after commit, the comment it was handed may still be rolled back:

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;

class IndexComment implements ShouldQueue, ShouldDispatchAfterCommit
{
    public function handle(CommentCreated $event): void
    {
        // ...
    }
}

What fires and what does not

The lifecycle events ride Eloquent's own model events, so anything that skips those skips these: saveQuietly(), the query builder, raw SQL. The moderation, reaction, attachment, and pin events are dispatched by the package itself, immediately after the write.

CommentUpdated is the one to watch for edits, because it fires for every kind of update. Ask the comment which kind it was:

Event::listen(CommentUpdated::class, function (CommentUpdated $event): void {
    if ($event->comment->wasChanged('body')) {
        // an edit, the revision for it is already filed
    }
});

The package's own listeners

Two subscribers are registered unconditionally, and both ask their question per comment rather than at boot, so a config change takes effect without a reboot:

  • MaintainsCommentCounts keeps an opted-in commentable's count honest. A model that never opted in costs an array lookup. See comment counts.
  • SendsReplyNotifications tells a comment's author that somebody replied. Nothing is sent unless the config says so. See reply notifications.

What to read next

  • Notifications to deliver approved-reply notifications.
  • Comment counts to maintain totals from lifecycle changes.
  • Testing to fake comment writes or assert real events.
PreviousComment countsNextReply notifications
View source

On this page

  1. The full list
  2. Lifecycle
  3. Moderation, base CommentModerated
  4. Reactions, base CommentReacted
  5. Attachments, base CommentAttachmentChanged
  6. Pinning, base CommentPinChanged
  7. Rule one: listen by concrete class
  8. Rule two: one event means one real change
  9. Queue anything slow
  10. What fires and what does not
  11. The package's own listeners
  12. What to read next