From f1cfa859e3826b2d996ffc8af30848bc537f06f9 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 12 Nov 2019 08:40:39 -0600 Subject: [PATCH] update Model dirty check (#30565) for attributes that are cast to any type of Object, the strict equivalency (`===`) will never return `true`, because even though the values may be equal, the Object there reference will be different. this changes checks if the cast type is either `object` or `collection`, both with return Objects, and uses loose equivalency to compare them. even though date casting also returns an Object, we don't need to handle that since it's handled in the previous conditional. the test represents a scenario that occurs when using JSON fields in MySQL. MySQL returns the value with spaces between the elements, but `json_encode` returns a string **without** spaces between the elements. --- .../Eloquent/Concerns/HasAttributes.php | 3 +++ tests/Database/DatabaseEloquentModelTest.php | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php index bb36a9c2506a..378c2d12a211 100644 --- a/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php +++ b/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php @@ -1172,6 +1172,9 @@ public function originalIsEquivalent($key, $current) } elseif ($this->isDateAttribute($key)) { return $this->fromDateTime($current) === $this->fromDateTime($original); + } elseif ($this->hasCast($key, ['object', 'collection'])) { + return $this->castAttribute($key, $current) == + $this->castAttribute($key, $original); } elseif ($this->hasCast($key)) { return $this->castAttribute($key, $current) === $this->castAttribute($key, $original); diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index cd47952849f0..12e3d06a8084 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -116,6 +116,23 @@ public function testDirtyOnCastOrDateAttributes() $this->assertTrue($model->isDirty('datetimeAttribute')); } + public function testDirtyOnCastedObjects() + { + $model = new EloquentModelCastingStub; + $model->setRawAttributes([ + 'objectAttribute' => '["one", "two", "three"]', + 'collectionAttribute' => '["one", "two", "three"]', + ]); + $model->syncOriginal(); + + $model->objectAttribute = ['one', 'two', 'three']; + $model->collectionAttribute = ['one', 'two', 'three']; + + $this->assertFalse($model->isDirty()); + $this->assertFalse($model->isDirty('objectAttribute')); + $this->assertFalse($model->isDirty('collectionAttribute')); + } + public function testCleanAttributes() { $model = new EloquentModelStub(['foo' => '1', 'bar' => 2, 'baz' => 3]);