After improving the casts in Maravel-Framework’s Eloquent and in laravel-crud-wizard-free as a retroactive feature-fix for Laravel/Lumen ≥ 8, before which I wrote this article as an alternative:
I realized that there is now another way to prevent updates in the model that I previously wrote about in this article:
The frozen model - read only DTO is not in fact a model. This new feature came in the version 5.1.0 of laravel-crud-wizard-free and enables the developer to prevent updates by tapping into the getDirty method which is now used on incrementOrDecrement and on save methods. When getDirty returns empty array, no update is performed.
Changes can still be done to the model but they will not be saved in DB until the model is unlocked. In all this time relations can be loaded into the model and even updated. Also only saving event will be fired because updating is fired after the isDirty check.
Real world scenarios for this feature would be:
- preventing multiple saving operations on a model and saving it only once at the end of the logic
- preventing save operations on the model while being passed as argument
- preventing save operations on a model that must never be updated which is easier done by declaring the php property as empty array:
protected ?array $tmpDirtyIfAttributesAreSyncedFromCashedCasts = [];
The new feature’s code from the library is this:
/**
* Prevent updates
* Note that relations can be loaded and updated during the lock
*/
public function lockUpdates(bool $checkDirty = true): bool
{
if (
!$this->exists
|| $this->tmpDirtyIfAttributesAreSyncedFromCashedCasts !== null
|| ($checkDirty && $this->isDirty())
) {
return false;
}
$this->tmpDirtyIfAttributesAreSyncedFromCashedCasts = [];
return true;
}
/**
* Unlock updates
*
* To reset the model's $attributes and get the changes from dirty applied during the lock use:
*
* if ($this->unlockUpdates()) {
* $dirty = $this->getDirty();
* $this->attributes = $this->original;
* $this->classCastCache = [];
* $this->attributeCastCache = [];
* }
*
* Note that relations can be loaded during the lock
*/
public function unlockUpdates(): bool
{
if ($this->hasUnlockedUpdates()) {
return false;
}
$this->tmpDirtyIfAttributesAreSyncedFromCashedCasts = null;
return true;
}
public function hasUnlockedUpdates(): bool
{
return $this->tmpDirtyIfAttributesAreSyncedFromCashedCasts !== [];
}
It will not be included into Maravel-Framework but it will work starting with version 10.53.2 if added in the project manually or via laravel-crud-wizard-free package.
Update 2025.11.11
The table can still be updated via raw queries like:
$parentModel->modelAsRelation()->update(['col' => 3]);
The above will bypass Eloquent.

Top comments (0)