Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/Concerns/HasRevisions.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ public static function revisioned(Closure $callback): void

/**
* Register a listener for the rollingBack event, which fires before a rollback is performed.
* Receives the model and the target revision; mutating the revision here affects the restore.
* Return false from the callback to abort the rollback.
*/
public static function rollingBack(Closure $callback): void
Expand All @@ -117,6 +118,7 @@ public static function rollingBack(Closure $callback): void

/**
* Register a listener for the rolledBack event, which fires after a rollback is performed.
* Receives the model and the revision it was rolled back to.
*/
public static function rolledBack(Closure $callback): void
{
Expand Down Expand Up @@ -280,7 +282,7 @@ public function rollback(): bool
*/
public function rollbackToRevision(RevisionContract $revision): bool
{
if ($this->fireModelEvent('rollingBack') === false) {
if ($this->fireRevisionEvent('rollingBack', true, $revision) === false) {
return false;
Comment on lines 283 to 286

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nopes, not relevant yet.

}

Expand All @@ -299,7 +301,7 @@ public function rollbackToRevision(RevisionContract $revision): bool
$this->saveAsRollbackRevision($options, $revision);
}

$this->fireModelEvent('rolledBack', false);
$this->fireRevisionEvent('rolledBack', false, $revision);

return $result;
}
Expand Down Expand Up @@ -465,6 +467,25 @@ protected function revisionToReplace(): ?Revision
return $this->latestRevision()->first();
}

/**
* Fire an observable event with the model and revision, dispatching directly instead of
* via fireModelEvent() so third-party overrides of that method (e.g. laravel-pivot-events)
* don't interfere.
*/
protected function fireRevisionEvent(string $event, bool $halt, RevisionContract $revision): mixed
{
$dispatcher = static::getEventDispatcher();

if (! $dispatcher) {
return true;
}

return $dispatcher->{$halt ? 'until' : 'dispatch'}(
"eloquent.{$event}: " . static::class,
[$this, $revision]
);
}

/**
* Save a rollback revision, always as a new record and marked as a rollback.
*/
Expand Down
69 changes: 64 additions & 5 deletions tests/RevisionEventsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,33 @@ public function it_fires_the_rolling_back_event_before_a_rollback()
}

#[Test]
public function it_fires_the_rolled_back_event_after_a_rollback()
public function it_passes_the_target_revision_to_the_rolling_back_listener()
{
// Given
$fired = false;
Post::rolledBack(function (Post $post) use (&$fired) {
$fired = true;
$capturedRevision = null;
Post::rollingBack(function (Post $post, Revision $revision) use (&$capturedRevision) {
$capturedRevision = $revision;
});
$post = $this->createPost();
$this->modifyPost($post);

$revision = $post->revisions()->firstOrFail();

// When
$post->rollbackToRevision($revision);

// Then
$this->assertTrue($revision->is($capturedRevision));
}

#[Test]
public function it_allows_the_rolling_back_listener_to_mutate_the_revision_before_restore()
{
// Given
Post::rollingBack(function (Post $post, Revision $revision) {
$metadata = $revision->metadata;
$metadata['attributes']['name'] = 'Overridden by listener';
$revision->metadata = $metadata;
});
$post = $this->createPost();
$this->modifyPost($post);
Expand All @@ -89,7 +110,7 @@ public function it_fires_the_rolled_back_event_after_a_rollback()
$post->rollbackToRevision($post->revisions()->firstOrFail());

// Then
$this->assertTrue($fired);
$this->assertEquals('Overridden by listener', $post->fresh()->name);
}

#[Test]
Expand All @@ -107,4 +128,42 @@ public function it_does_not_roll_back_when_the_rolling_back_event_returns_false(
$this->assertFalse($result);
$this->assertEquals('Another post name', $post->fresh()->name);
}

#[Test]
public function it_fires_the_rolled_back_event_after_a_rollback()
{
// Given
$fired = false;
Post::rolledBack(function (Post $post) use (&$fired) {
$fired = true;
});
$post = $this->createPost();
$this->modifyPost($post);

// When
$post->rollbackToRevision($post->revisions()->firstOrFail());

// Then
$this->assertTrue($fired);
}

#[Test]
public function it_passes_the_target_revision_to_the_rolled_back_listener()
{
// Given
$capturedRevision = null;
Post::rolledBack(function (Post $post, Revision $revision) use (&$capturedRevision) {
$capturedRevision = $revision;
});
$post = $this->createPost();
$this->modifyPost($post);

$revision = $post->revisions()->firstOrFail();

// When
$post->rollbackToRevision($revision);

// Then
$this->assertTrue($revision->is($capturedRevision));
}
}