Skip to content

Commit 00f4323

Browse files
committed
[11.x] Add "previous" state to models
1 parent 16c01c2 commit 00f4323

File tree

6 files changed

+115
-2
lines changed

6 files changed

+115
-2
lines changed

src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php

+45
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ trait HasAttributes
6161
*/
6262
protected $original = [];
6363

64+
/**
65+
* The model attribute's previous state.
66+
*
67+
* @var array
68+
*/
69+
protected $previous = [];
70+
6471
/**
6572
* The changed model attributes.
6673
*
@@ -1959,6 +1966,32 @@ public function getRawOriginal($key = null, $default = null)
19591966
return Arr::get($this->original, $key, $default);
19601967
}
19611968

1969+
/**
1970+
* Get the model's previous attribute values.
1971+
*
1972+
* @param string|null $key
1973+
* @param mixed $default
1974+
* @return mixed|array
1975+
*/
1976+
public function getPrevious($key = null, $default = null)
1977+
{
1978+
return (new static)->setRawAttributes(
1979+
$this->previous, $sync = true
1980+
)->getOriginalWithoutRewindingModel($key, $default);
1981+
}
1982+
1983+
/**
1984+
* Get the model's raw previous attribute values.
1985+
*
1986+
* @param string|null $key
1987+
* @param mixed $default
1988+
* @return mixed|array
1989+
*/
1990+
public function getRawPrevious($key = null, $default = null)
1991+
{
1992+
return Arr::get($this->previous, $key, $default);
1993+
}
1994+
19621995
/**
19631996
* Get a subset of the model's attributes.
19641997
*
@@ -2018,6 +2051,18 @@ public function syncOriginalAttributes($attributes)
20182051
return $this;
20192052
}
20202053

2054+
/**
2055+
* Sync the previous attributes with the original.
2056+
*
2057+
* @return $this
2058+
*/
2059+
public function syncPrevious()
2060+
{
2061+
$this->previous = $this->original;
2062+
2063+
return $this;
2064+
}
2065+
20212066
/**
20222067
* Sync the changed attributes.
20232068
*

src/Illuminate/Database/Eloquent/Model.php

+4
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,8 @@ protected function incrementOrDecrement($column, $amount, $extra, $method)
989989
$amount = (clone $this)->setAttribute($column, $amount)->getAttributeFromArray($column);
990990
}
991991

992+
$this->syncPrevious();
993+
992994
return tap($this->setKeysForSaveQuery($this->newQueryWithoutScopes())->{$method}($column, $amount, $extra), function () use ($column) {
993995
$this->syncChanges();
994996

@@ -1236,6 +1238,8 @@ protected function performUpdate(Builder $query)
12361238
$dirty = $this->getDirtyForUpdate();
12371239

12381240
if (count($dirty) > 0) {
1241+
$this->syncPrevious();
1242+
12391243
$this->setKeysForSaveQuery($query)->update($dirty);
12401244

12411245
$this->syncChanges();

tests/Integration/Database/EloquentDeleteTest.php

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ protected function afterRefreshingDatabase()
3333
});
3434
}
3535

36+
public function testBasicDelete()
37+
{
38+
$post = Post::create();
39+
40+
Post::where('id', $post->id)->delete();
41+
42+
$this->assertCount(0, Post::all());
43+
}
44+
3645
public function testDeleteWithLimit()
3746
{
3847
if ($this->driver === 'sqlsrv') {

tests/Integration/Database/EloquentModelRefreshTest.php

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ public function testItSyncsOriginalOnRefresh()
5454
$this->assertSame('patrick', $post->getOriginal('title'));
5555
}
5656

57+
public function testItDoesNotSyncPreviousOnRefresh()
58+
{
59+
$post = Post::create(['title' => 'pat']);
60+
61+
Post::find($post->id)->update(['title' => 'patrick']);
62+
63+
$post->refresh();
64+
65+
$this->assertEmpty($post->getDirty());
66+
$this->assertEmpty($post->getPrevious());
67+
}
68+
5769
public function testAsPivot()
5870
{
5971
Schema::create('post_posts', function (Blueprint $table) {

tests/Integration/Database/EloquentModelTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,14 @@ public function testDiscardChanges()
8484
$this->assertFalse($user->wasChanged());
8585
$this->assertSame($originalName, $user->getOriginal('name'));
8686
$this->assertSame($overrideName, $user->getAttribute('name'));
87+
$this->assertEmpty($user->getPrevious());
8788

8889
$user->discardChanges();
8990

9091
$this->assertEmpty($user->getDirty());
9192
$this->assertSame($originalName, $user->getOriginal('name'));
9293
$this->assertSame($originalName, $user->getAttribute('name'));
94+
$this->assertEmpty($user->getPrevious());
9395

9496
$user->save();
9597
$this->assertFalse($user->wasChanged());

tests/Integration/Database/EloquentUpdateTest.php

+43-2
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ public function testBasicUpdate()
4141
'title' => 'Ms.',
4242
]);
4343

44-
TestUpdateModel1::where('title', 'Ms.')->delete();
44+
TestUpdateModel1::where('title', 'Ms.')->update(['title' => 'Dr.']);
4545

46-
$this->assertCount(0, TestUpdateModel1::all());
46+
$this->assertSame('Dr.', TestUpdateModel1::first()->title);
4747
}
4848

4949
public function testUpdateWithLimitsAndOrders()
@@ -136,6 +136,47 @@ public function testIncrementOrDecrementIgnoresGlobalScopes()
136136
$deletedModel->decrement('counter');
137137
$this->assertEquals(0, $deletedModel->fresh()->counter);
138138
}
139+
140+
public function testUpdateSyncsPrevious()
141+
{
142+
$model = TestUpdateModel1::create([
143+
'name' => Str::random(),
144+
'title' => 'Ms.',
145+
]);
146+
147+
$model->update(['title' => 'Dr.']);
148+
149+
$this->assertSame('Dr.', $model->title);
150+
$this->assertSame('Dr.', $model->getOriginal('title'));
151+
$this->assertSame('Ms.', $model->getPrevious('title'));
152+
}
153+
154+
public function testSaveSyncsPrevious()
155+
{
156+
$model = TestUpdateModel1::create([
157+
'name' => Str::random(),
158+
'title' => 'Ms.',
159+
]);
160+
161+
$model->title = 'Dr.';
162+
$model->save();
163+
164+
$this->assertSame('Dr.', $model->title);
165+
$this->assertSame('Dr.', $model->getOriginal('title'));
166+
$this->assertSame('Ms.', $model->getPrevious('title'));
167+
}
168+
169+
public function testIncrementSyncsPrevious()
170+
{
171+
$model = TestUpdateModel3::create([
172+
'counter' => 0,
173+
]);
174+
175+
$model->increment('counter');
176+
177+
$this->assertEquals(1, $model->counter);
178+
$this->assertEquals(0, $model->getPrevious('counter'));
179+
}
139180
}
140181

141182
class TestUpdateModel1 extends Model

0 commit comments

Comments
 (0)