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

Edits and revisions.

Record append-only revision history when a comment body is edited.

Editing a comment's body records what it used to say. That happens through Eloquent's model events, so it covers edit(), update(), and a plain attribute save alike.

Edit a comment

$comment->edit('A clearer version', by: $user);

edit() records the editor and returns whether anything was saved. Submitting the existing body writes no change and records no revision.

A plain save records the same history with a null editor, because the package will not invent an actor:

$comment->update(['body' => 'A clearer version']);

$comment->body = 'A clearer version';
$comment->save();

A null editor is ordinary: a console command, a queued job, and an import all change a body with nobody to name.

Read the history

$comment->edited_at;   // when the body last changed, or null
$comment->revisions;   // prior bodies, oldest first

Each revision holds the body as it stood before that edit, plus the editor when one was named:

foreach ($comment->revisions as $revision) {
    $revision->body;      // what it used to say
    $revision->editor;    // the model that made the change, or null
    $revision->created_at;
}

The current body lives on the comment. A comment edited twice has two revisions: the original text and the intermediate one.

editor is an ordinary polymorphic relation, so with('revisions.editor') loads a whole history's authors in one go.

What counts as an edit

Only a body change is gated, stamps edited_at, and earns a revision. Moderating, pinning, restoring, and every other update leave all three alone: they change what the package knows about the comment, not what its author wrote.

Saving the same body over itself is not an edit either.

What is not recorded

Recording rides Eloquent's model events, so anything that skips them skips this:

$comment->body = 'Rewritten';
$comment->saveQuietly();                                    // no revision

Comment::query()->where('id', $comment->id)
    ->update(['body' => 'Rewritten']);                      // no revision

That is the same seam comment counts ride, and the same caveat applies: writes that go around Eloquent go around the package.

The gates an edit shares

An edit passes the same checks a write does, and two of its own:

  • comments.max_length is re-checked, so an edit cannot do what a write was refused. Exceeding it throws a BodyTooLongException.
  • A tombstone is refused. Editing a soft-deleted comment throws a CommentTrashedException, a comment kept as history that can still be rewritten is not history.
  • An unsaved comment is refused, with a LogicException. Persist it first.
  • A comment loaded without its body is refused, also with a LogicException. The one job of a revision is to say what the comment used to hold, and a comment selected without body cannot answer that. Reload it, or select the column.

Revisions are append-only

The CommentRevision model refuses updates outright:

use ByRcsc\LaravelComments\Exceptions\RevisionIsAppendOnlyException;

$revision->update(['body' => 'Never happened']);  // throws

The refusal is a model event, so every path through Eloquent hits it. Deleting a revision is still allowed, and there is no updated_at column, a row that can never be updated has no honest second timestamp to keep.

This is a convention, not a proof. There is no hash chain here and no tamper evidence: the rows are ordinary rows, and anything with database access can edit them. An application that needs a verifiable history wants Laravel Approval.

Deletion

Soft deleting a comment keeps its revisions, which is what lets a moderator read what a removed comment used to say. Force deleting removes them through the cascade, for the comment and its whole subtree.

Re-moderating an edit

CommentUpdated fires after the revision for that edit is filed, so a listener can compare the new body against what the comment said before and send it back to the queue. See moderation.

Seed history in a test

use ByRcsc\LaravelComments\Models\CommentRevision;

CommentRevision::factory()->forComment($comment)->by($editor)->create();

That is for tests about reading history. A test about recording it should edit the comment and let the engine file the row.

What to read next

  • Commentable models for the comment relationships used around revisions.
  • Events and listeners to react after a comment body changes.
  • Testing to build revision history with factories.
PreviousReactionsNextAttachments
View source

On this page

  1. Edit a comment
  2. Read the history
  3. What counts as an edit
  4. What is not recorded
  5. The gates an edit shares
  6. Revisions are append-only
  7. Deletion
  8. Re-moderating an edit
  9. Seed history in a test
  10. What to read next