›
byrcsc/laravel-comments · 1.x
Keep a tombstone after soft deletion or remove an entire reply subtree permanently.
Comments soft delete. delete() marks a tombstone, restore() brings it back,
and forceDelete() removes it for good, along with everything below it in the
thread.
$comment->delete();The comment leaves every ordinary query through Eloquent's global scope. What it leaves behind is deliberate:
CommentTrashedException on a tombstone. A record
kept as history that can still be changed is not history.Reading is never gated. reactionSummary(), revisions, and attachments all
work on a tombstone.
Read tombstones back the usual way:
Comment::withTrashed()->find($id);
Comment::onlyTrashed()->get();$comment->restore();The comment comes back exactly as it was, status included, and its reactions and attachments become writable again.
$comment->forceDelete();This removes the comment and its entire reply subtree, through the
database's cascade on parent_id, soft-deleted replies included, because the
cascade does not care whether a row was a tombstone. Each removed comment takes
its own reactions, revisions, and attachment rows with it through their own
cascades.
Ancestors are untouched. Force deleting a mid-thread comment leaves the comment it replied to exactly where it was.
The database's cascade fires no model events. The replies it removes dispatch
nothing of their own, no CommentDeleted, no CommentForceDeleted.
The package works around that in the two places it matters, and you may have to in a third:
Attachments. Before the cascade runs, the package walks the whole subtree
and fires AttachmentRemoved once per attachment on it. A file-cleanup listener
sees files on the deleted comment and every reply below it.
Counts. CommentForceDeleted carries $countableRemoved: how many comments
in that subtree were approved and not soft deleted at the moment they went, this
one included. It cannot be recovered afterwards, by the time the event fires,
the rows are gone.
use ByRcsc\LaravelComments\Events\CommentForceDeleted;
use Illuminate\Support\Facades\Event;
Event::listen(CommentForceDeleted::class, function (CommentForceDeleted $event): void {
$this->stats->decrement('comments', $event->countableRemoved);
});Anything else you keep per comment. A search index, an activity feed, a
cache key, a listener that must see every removed row has to walk the subtree
itself, before calling forceDelete().
| Call | Events |
|---|---|
delete() | CommentDeleted |
restore() | CommentRestored |
forceDelete() | CommentDeleted, then CommentForceDeleted |
CommentDeleted fires on both kinds of delete, mirroring Eloquent's own
deleted event. Listen to CommentForceDeleted when only permanent removal
matters.
Both fire for the comment you called the method on. Neither fires for the replies the cascade takes.
The denormalized count follows all three operations: a soft delete subtracts an
approved comment, a restore adds it back if it is still approved, and a force
delete subtracts the whole subtree in one atomic step using
$countableRemoved. See comment counts.
delete allows the comment's own author by default. restore and forceDelete
both deny for everybody:
See authorization.