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
16 changes: 6 additions & 10 deletions src/Concerns/HasRevisions.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,16 @@ 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.
* Register a listener for the rollingBack event. Return false to abort the rollback;
* mutate the passed revision to change what gets restored.
*/
public static function rollingBack(Closure $callback): void
{
static::registerModelEvent('rollingBack', $callback);
}

/**
* 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.
* Register a listener for the rolledBack event, fired after a rollback with the target revision.
*/
public static function rolledBack(Closure $callback): void
{
Expand Down Expand Up @@ -163,8 +161,7 @@ public function latestRevision(): MorphOne
}

/**
* Compare the current model state against the latest revision or a specific revision, or against
* nothing if it has no revisions at all.
* Compare the current model state against the latest revision, a specific revision, or nothing.
*/
public function diff(?RevisionContract $revision = null): Diff
{
Expand Down Expand Up @@ -468,9 +465,8 @@ protected function revisionToReplace(): ?Revision
}

/**
* 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.
* Fire an event with the model and revision, bypassing fireModelEvent() so overrides of it
* (e.g. laravel-pivot-events) can't interfere.
*/
protected function fireRevisionEvent(string $event, bool $halt, RevisionContract $revision): mixed
{
Expand Down
9 changes: 8 additions & 1 deletion src/Revisioner.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,14 @@ protected function buildChanges(Revision $revision, ?Revision $baseline = null):
*/
protected function buildModelData(): array
{
return $this->filterModelData($this->model->getAttributes());
$attributes = $this->model->getAttributes();

if ($this->model->wasRecentlyCreated) {
// Backfill DB-assigned defaults without discarding unsaved in-memory changes.
$attributes += $this->model->fresh()?->getAttributes() ?? [];
}

return $this->filterModelData($attributes);
}

/**
Expand Down
29 changes: 29 additions & 0 deletions tests/CreatingRevisionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,35 @@ public function getRevisionOptions(): RevisableOptions
$this->assertTrue(Revision::first()->isInitial());
}

#[Test]
public function it_captures_database_default_values_on_the_initial_revision()
{
// Given
$post = new class extends Post
{
public function getRevisionOptions(): RevisableOptions
{
return parent::getRevisionOptions()->enableRevisionOnCreate();
}
};

$author = $this->createAuthor();

// When
$post = $post->create([
'author_id' => $author->id,
'name' => 'Post name',
'slug' => 'post-slug',
]);

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

$this->assertEquals(0, $revision->metadata['attributes']['votes']);
$this->assertEquals(0, $revision->metadata['attributes']['views']);
$this->assertNull($revision->metadata['attributes']['content']);
}

#[Test]
public function it_only_tags_the_first_revision_as_initial_when_reusing_the_same_instance()
{
Expand Down
23 changes: 23 additions & 0 deletions tests/RevisionDiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,29 @@ public function it_diffs_against_nothing_when_the_model_has_no_revisions()
$this->assertEquals(10, $changes['votes']['after']);
}

#[Test]
public function it_preserves_unsaved_changes_when_diffing_the_same_instance_right_after_creation()
{
// Given
$author = $this->createAuthor();

$post = Post::create([
'author_id' => $author->id,
'name' => 'Post name',
'slug' => 'post-slug',
'content' => 'Post content',
'votes' => 10,
'views' => 100,
]);

// When
$post->votes = 999;
$diff = $post->diff();

// Then
$this->assertEquals(999, $diff->all()['votes']['after']);
}

// relations

#[Test]
Expand Down