-
-
Notifications
You must be signed in to change notification settings - Fork 227
Description
Winter CMS Build
1.2
PHP Version
8.3
Database engine
MySQL/MariaDB
Plugins installed
No response
Issue description
When a beforeSave method is defined on a Model, it is called before anything that may be bound to the internal "model.beforeSave" event. This is especially apparent with the Winter\Storm\Database\Traits\Nullable trait, where it is counterintuitive to receive an empty string for a nullable property in beforeSave.
This behavior did change in wintercms/storm#192. When doing this change, I thought that the only difference between bindEvent and bindEventOnce would be the fact that the later is removed after being run.
It turns out that they have a very different behavior regarding priority: events bound "once" are always triggered before any other event and don't support priority at all. Here is the code doing this.
I see that @mjauvin did some work about this in wintercms/storm#200. The PR was closed automatically, not sure if it is still planned but it could help to fix this issue.
Alternatively, the first fix in #1251 (comment) could work better than the current one. Its drawbacks at the time were the following:
beforeValidatemethods added using$model->addDynamicMethod()would not be called. I may be wrong, but I think it sould be quite uncommon: (a) it only works for some trait methods, most other ones have an empty implementation in the base Model class that cannot be overridden using addDynamicMethod. (b) This could conflict with another plugin doing the same or if the extended model is updated. (c)Themodel.beforeValidateevent can be used instead and doesn't have these problems.- It changes the order in which events are triggered. That's what happened with the current fix, but this one would allow to set a priority to mitigate the issue without touching the Event Emitter.
Note: issue #1360 has the same root cause.
Steps to replicate
Take a model that uses the Nullable trait and has a beforeSave metod, for example:
class Test extends Model
{
public $table = 'tests';
use Winter\Storm\Database\Traits\Nullable;
public array $nullable = ['foo'];
public function beforeSave()
{
$this->foo ??= 'bar';
}
}Add foo as a text field to the fields.yaml.
In the backend, try to leave foo empty and save. Because the Nullable trait has not run yet, foo === '' instead of null and the value stays '' instead of defaulting to 'bar'.
Workaround
Manually calling the nullableBeforeSave() of the Nullable trait when needed can help. The null checks will still occur a second time later, but it shouldn't be an issue.
function beforeSave()
{
$this->nullableBeforeSave();
$this->foo ??= 'bar';
}Reverting wintercms/storm@6afe7a0 would also work, but will reintroduce the bug it fixed.