diff --git a/src/Eloquent/DocumentModel.php b/src/Eloquent/DocumentModel.php index f8d399e62..8051858e0 100644 --- a/src/Eloquent/DocumentModel.php +++ b/src/Eloquent/DocumentModel.php @@ -43,6 +43,7 @@ use function is_string; use function ltrim; use function method_exists; +use function serialize; use function sprintf; use function str_contains; use function str_starts_with; @@ -377,6 +378,10 @@ public function originalIsEquivalent($key) $this->castAttribute($key, $original); } + if ($this->isClassCastable($key)) { + return serialize($attribute) === serialize($original); + } + return is_numeric($attribute) && is_numeric($original) && strcmp((string) $attribute, (string) $original) === 0; } diff --git a/tests/ModelTest.php b/tests/ModelTest.php index 8fcd0b272..7e3445492 100644 --- a/tests/ModelTest.php +++ b/tests/ModelTest.php @@ -26,6 +26,7 @@ use MongoDB\Laravel\Tests\Models\Item; use MongoDB\Laravel\Tests\Models\MemberStatus; use MongoDB\Laravel\Tests\Models\NonIncrementing; +use MongoDB\Laravel\Tests\Models\Options; use MongoDB\Laravel\Tests\Models\Soft; use MongoDB\Laravel\Tests\Models\SqlUser; use MongoDB\Laravel\Tests\Models\User; @@ -1075,6 +1076,22 @@ public function testGetDirtyDates(): void $this->assertEmpty($user->getDirty()); } + public function testGetDirtyObjects(): void + { + $user = new User(); + $user->options = new Options(); + $this->assertNotEmpty($user->getDirty()); + + $user->save(); + $this->assertEmpty($user->getDirty()); + + $user->options = (new Options())->setOption1('Value1'); + $this->assertNotEmpty($user->getDirty()); + + $user->save(); + $this->assertEmpty($user->getDirty()); + } + public function testChunkById(): void { User::create(['name' => 'fork', 'tags' => ['sharp', 'pointy']]); diff --git a/tests/Models/Options.php b/tests/Models/Options.php new file mode 100644 index 000000000..ce9a2c424 --- /dev/null +++ b/tests/Models/Options.php @@ -0,0 +1,37 @@ +option1 = $option1; + return $this; + } + + public function setOption2(string $option2): self + { + $this->option2 = $option2; + return $this; + } + + public function serialize(): object + { + $result = []; + if (isset($this->option1)) { + $result['option1'] = $this->option1; + } + + if (isset($this->option2)) { + $result['option2'] = $this->option2; + } + + return (object) $result; + } +} diff --git a/tests/Models/OptionsCast.php b/tests/Models/OptionsCast.php new file mode 100755 index 000000000..9a427ad1d --- /dev/null +++ b/tests/Models/OptionsCast.php @@ -0,0 +1,40 @@ +setOption1($value['option1']); + } + + if (! empty($value['option2'])) { + $attributes->setOption2($value['option2']); + } + + return $attributes; + } + + /** + * @param Model $model + * @param string $key + * @param Options|null $value + * @param array $attributes + * + * @return null[]|object[] + */ + public function set(Model $model, string $key, mixed $value, array $attributes): array + { + return [ + $key => $value?->serialize(), + ]; + } +} diff --git a/tests/Models/User.php b/tests/Models/User.php index 5b8ac983a..257a2dd34 100644 --- a/tests/Models/User.php +++ b/tests/Models/User.php @@ -29,6 +29,7 @@ * @property Carbon $updated_at * @property string $username * @property MemberStatus member_status + * @property Options $options */ class User extends Model implements AuthenticatableContract, CanResetPasswordContract { @@ -44,6 +45,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon 'birthday' => 'datetime', 'entry.date' => 'datetime', 'member_status' => MemberStatus::class, + 'options' => OptionsCast::class, ]; protected $fillable = [