›
byrcsc/laravel-comments · 1.x
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.
| Event | Fires when |
|---|---|
CommentCreated | A comment or reply is written, guest or not |
CommentUpdated | Any saved change: a body edit, a transition, a pin, a restore |
CommentDeleted | A soft delete, and also a force delete |
CommentRestored | A tombstone is restored |
CommentForceDeleted | A 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.
CommentModerated| Event | Fires when |
|---|---|
CommentApproved | approve() moved it |
CommentRejected | reject() moved it |
CommentMarkedAsSpam | markAsSpam() moved it |
Each carries $previousStatus and $actor (null when nobody was named).
CommentReacted| Event | Fires when |
|---|---|
ReactionAdded | A reaction row actually appeared |
ReactionRemoved | One actually went away |
Each carries $reactor and $reaction.
CommentAttachmentChanged| Event | Fires when |
|---|---|
AttachmentAdded | A row is recorded |
AttachmentRemoved | detach() removes one, or a force delete takes one |
Each carries $attachment.
CommentPinChanged| Event | Fires when |
|---|---|
CommentPinned | A pin actually went up |
CommentUnpinned | One actually came down |
Each carries $actor.
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.
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.
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
{
// ...
}
}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
}
});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.