Skip to content

Fix save() leaving the model dirty after an insert - #392

Merged
techmahedy merged 3 commits into
doppar:4.xfrom
techmahedy:issue
Oct 30, 2026
Merged

techmahedy merged 3 commits into
doppar:4.xfrom
techmahedy:issue

Conversation

@techmahedy

Copy link
Copy Markdown
Member

Pull Request Checklist

Q A
Branch? 4.x
Bug fix? yes
New feature? no
Deprecations? no
Issues -
License MIT

Description

After a model was inserted, save() never synced originalAttributes, so the model still looked entirely new. The update branch of save() and find() both sync it, which made a freshly inserted model the one case that stayed dirty.

Impact

create(), firstOrCreate(), updateOrCreate(), createFromModel() and any new Model followed by save() all go through this path. After the insert:

  • getDirtyAttributes() reported every attribute, including the primary key, and getOriginal() returned null.
  • The next save() on the same instance ran an UPDATE that rewrote every column (and updated_at) with the values it had just inserted. It also fired after_updated and the property watchers for a row that hadn't changed.

A common trigger is $post = Post::create([...]); $post->tags()->relate(...); $post->save();, or any model saved twice in one request.

Changes

  • save() now syncs originalAttributes after a successful insert, including when hooks are suppressed with withoutHook().
  • The sync runs after after_created, so hooks see the same model state as before. This matches the update branch.
  • Added 13 tests in EntityModelHookTest. They cover a clean model after save(), create(), quiet inserts, firstOrCreate() and updateOrCreate(). They also check that a save() right after an insert fires no after_updated and doesn't rewrite the row, that later edits are the only dirty attributes, that after_created behavior is unchanged, and that inserted models don't share state.

Backward Compatibility

Hook and watcher behavior during after_created is unchanged. The only difference is that an inserted model is no longer reported dirty. Code that relied on the stale dirty state to write the same values again would stop doing so, which is the intended fix.

Checklist

  • Tests have been added or updated
  • Documentation has been updated if necessary
  • Code follows the project coding standards
  • All tests pass locally

@techmahedy techmahedy added the bug Something isn't working label Oct 30, 2026
@techmahedy
techmahedy merged commit b8dc97f into doppar:4.x Oct 30, 2026
13 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant