Fix scalar type change not detected as dirty in MongoDB models#3501
Open
GromNaN wants to merge 8 commits into
Open
Fix scalar type change not detected as dirty in MongoDB models#3501GromNaN wants to merge 8 commits into
GromNaN wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes MongoDB Eloquent “dirty” detection so scalar type changes (e.g., 1 → '1') are treated as dirty, aligning comparisons with BSON/MongoDB’s type-preserving storage semantics.
Changes:
- Reworks
DocumentModel::originalIsEquivalent()to be BSON-aware (strict for scalars; BSON-serialization comparison for non-scalars). - Updates existing dirty-date test setup to use normal attribute assignment +
syncOriginal(). - Adds tests covering scalar type changes and embedded document (subdocument) equality.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/Eloquent/DocumentModel.php |
Replaces SQL-oriented equivalence logic with scalar strictness + BSON-based comparison for complex values. |
tests/ModelTest.php |
Adds/adjusts tests to validate the new dirty detection behavior for dates, scalar type changes, and embedded docs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
a61aae0 to
28caed9
Compare
paulinevos
reviewed
May 4, 2026
paulinevos
reviewed
May 4, 2026
alcaeus
reviewed
May 19, 2026
|
|
||
| if ($this->hasCast($key, static::$primitiveCastTypes)) { | ||
| return $this->castAttribute($key, $attribute) === | ||
| $this->castAttribute($key, $original); |
Member
There was a problem hiding this comment.
Should casts be applied before we convert to BSON?
Co-authored-by: Hamid Alaei <halaei@users.noreply.github.com> Co-authored-by: divine <divine@users.noreply.github.com>
- Extract all dirty detection tests from ModelTest into ModelGetDirtyTest - Add testGetDirtyDateWithoutCast: covers UTCDateTime fields without cast, comparing via Document::fromPHP after DateTimeInterface conversion
For fields with a primitive cast (int, bool, string, etc.), apply the
cast before comparing values, preserving Eloquent's behavior where
int(1) and string('1') are equivalent on a field cast to int.
Date casts are excluded from this path and continue to use the
DateTimeInterface -> UTCDateTime -> Document comparison.
… tests Extract scalar cast types into a static property ($scalarCastTypes) to make the intent explicit. Add encrypted cast variants so re-assigning the same plaintext is not considered dirty (Eloquent encrypts on SET, castAttribute decrypts before comparing). Object/array/collection/json casts are excluded from this path and fall through to the BSON Document comparison, which correctly handles PHP-level type differences (array vs stdClass).
…alent Replace the explicit scalar inclusion list with a shorter exclusion list ($nonScalarCastTypes) of types that produce non-scalar PHP values. Any new scalar type added to Eloquent's $primitiveCastTypes is automatically covered. Date types are excluded via isDateAttribute() so future date cast additions are also covered without touching this class.
942debd to
bca6952
Compare
createSearchIndex triggers Atlas Search engine (mongot) initialization, which causes a brief mongod unavailability. Add a final ping loop to ensure MongoDB is ready again before the subsequent steps run.
b26711f to
17c2ded
Compare
17c2ded to
25a8f06
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes a bug in
originalIsEquivalent()where changing a field fromint(1)tostring('1')was not reported as dirty, even though MongoDB stores types as-is and the document would change on the next save.Changes
Replace the SQL-oriented logic (numeric coercion via
is_numeric+strcmp, date/cast special cases) with BSON-aware comparison:===early return; any non-identical scalar pair returnsfalse(dirty).Document::fromPHP()serialization, which mirrors what MongoDB actually stores.This is a breaking change: code that was relying on
int(1)andstring('1')being equivalent will now see them as dirty.Supersede #2515
This is a rework of #2515. The approach is the same but adapted to the current codebase (
DocumentModeltrait,MongoDB\Laravelnamespace,Document::fromPHP()instead of the deprecatedfromPHP()global function).