Skip to content

Commit 91e3cb5

Browse files
GromNaNhalaeidivine
committed
Fix scalar type change not detected as dirty in MongoDB models
Co-authored-by: Hamid Alaei <halaei@users.noreply.github.com> Co-authored-by: divine <divine@users.noreply.github.com>
1 parent 6160526 commit 91e3cb5

2 files changed

Lines changed: 44 additions & 22 deletions

File tree

src/Eloquent/DocumentModel.php

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Illuminate\Support\Str;
2121
use MongoDB\BSON\Binary;
2222
use MongoDB\BSON\Decimal128;
23+
use MongoDB\BSON\Document;
2324
use MongoDB\BSON\ObjectID;
2425
use MongoDB\BSON\Type;
2526
use MongoDB\BSON\UTCDateTime;
@@ -39,15 +40,13 @@
3940
use function func_get_args;
4041
use function in_array;
4142
use function is_array;
42-
use function is_numeric;
43-
use function is_object;
43+
use function is_scalar;
4444
use function is_string;
4545
use function ltrim;
4646
use function method_exists;
4747
use function sprintf;
4848
use function str_contains;
4949
use function str_starts_with;
50-
use function strcmp;
5150
use function strlen;
5251
use function trigger_error;
5352
use function var_export;
@@ -393,26 +392,11 @@ public function originalIsEquivalent($key)
393392
return false;
394393
}
395394

396-
if ($this->isDateAttribute($key)) {
397-
$attribute = $attribute instanceof UTCDateTime ? $this->asDateTime($attribute) : $attribute;
398-
$original = $original instanceof UTCDateTime ? $this->asDateTime($original) : $original;
399-
400-
// Comparison on DateTimeInterface values
401-
// phpcs:disable SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedEqualOperator
402-
return $attribute == $original;
403-
}
404-
405-
if ($this->hasCast($key, static::$primitiveCastTypes)) {
406-
return $this->castAttribute($key, $attribute) ===
407-
$this->castAttribute($key, $original);
408-
}
409-
410-
if ($this->isClassCastable($key)) {
411-
return ! is_object($attribute) ? $attribute === $original : $attribute == $original;
395+
if (is_scalar($attribute) || is_scalar($original)) {
396+
return false;
412397
}
413398

414-
return is_numeric($attribute) && is_numeric($original)
415-
&& strcmp((string) $attribute, (string) $original) === 0;
399+
return (string) Document::fromPHP(['v' => $attribute]) === (string) Document::fromPHP(['v' => $original]);
416400
}
417401

418402
/** @inheritdoc */

tests/ModelTest.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,9 @@ public function testMultipleLevelDotNotation(): void
10591059
public function testGetDirtyDates(): void
10601060
{
10611061
$user = new User();
1062-
$user->setRawAttributes(['name' => 'John Doe', 'birthday' => new DateTime('19 august 1989')], true);
1062+
$user->name = 'John Doe';
1063+
$user->birthday = new DateTime('19 august 1989');
1064+
$user->syncOriginal();
10631065
$this->assertEmpty($user->getDirty());
10641066

10651067
$user->birthday = new DateTime('19 august 1989');
@@ -1082,6 +1084,42 @@ public function testGetDirtyObjects(): void
10821084
$this->assertEmpty($user->getDirty());
10831085
}
10841086

1087+
public function testGetDirtyScalarTypeChange(): void
1088+
{
1089+
// Changing a scalar value from one type to another must be considered dirty
1090+
// because MongoDB stores types as-is (int 1 and string '1' are different).
1091+
$user = new User();
1092+
$user->name = 'John Doe';
1093+
$user->age = 25;
1094+
$user->syncOriginal();
1095+
1096+
$this->assertEmpty($user->getDirty());
1097+
1098+
// Same value, same type: not dirty
1099+
$user->age = 25;
1100+
$this->assertEmpty($user->getDirty());
1101+
1102+
// Same numeric value, different type: dirty
1103+
$user->age = '25';
1104+
$this->assertTrue($user->isDirty('age'));
1105+
}
1106+
1107+
public function testGetDirtyEmbeddedDocument(): void
1108+
{
1109+
$user = User::create(['name' => 'John Doe', 'address' => ['city' => 'Paris', 'country' => 'France']]);
1110+
1111+
$user = User::find($user->id);
1112+
$this->assertFalse($user->isDirty());
1113+
1114+
// Setting the same array value: not dirty
1115+
$user->address = ['city' => 'Paris', 'country' => 'France'];
1116+
$this->assertFalse($user->isDirty());
1117+
1118+
// Changing a nested value: dirty
1119+
$user->address = ['city' => 'Lyon', 'country' => 'France'];
1120+
$this->assertTrue($user->isDirty('address'));
1121+
}
1122+
10851123
public function testChunkById(): void
10861124
{
10871125
User::create(['name' => 'fork', 'tags' => ['sharp', 'pointy']]);

0 commit comments

Comments
 (0)