wintercms / wintercms/winter

Changes made in settings model's beforeSave function are lost immediately afterward

Open
#1,360 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
PHP
Stars
1.5k
Forks
246
Avg merge
19h 2m
Merged PRs (30d)
7

Description

Winter CMS Build

1.2

PHP Version

8.3

Database engine

MySQL/MariaDB

Plugins installed

No response

Issue description

While testing the upgrade from Winter CMS 1.2.3 to 1.2.7, an automated test began failing because changes to a settings model in its beforeSave function are lost immediately after the beforeSave function returns. I have traced it down to the Winter\Storm\Database\Model::bootNicerEvents function where it fires $model->fireEvent('model.beforeSave').

I added a dump at the end of the beforeSave and the attributes are what I expect. I added a dump just after this event is fired and the attributes are reverted.

After Luke's hint to look at this commit, I changed bindEventOnce to bindEvent in the bootNicerEvents function and it started working as expected, so this commit is the likely cause of the issue. However, the root cause may be something deeper inside of the bindEventOnce function specifically for settings models.

Steps to replicate
  1. Create a settings model with some simple text fields.
    1. Run something like php artisan create:settings Company.Plugin TestSettings
    2. Add the following to the fields.yaml file:
fields:
    foo:
        label: Foo
        type: text
    bar:
        label: Bar
        type: text
  1. Register the backend settings page as needed to point to this new settings model.
            'test'  => [
                'label'       => 'Test Settings',
                'description' => 'Test settings.',
                'icon'        => 'icon-lock',
                'category'    => 'Testing',
                'class'       => 'Company\Plugin\Models\TestSettings',
                'order'       => 30,
                'keywords'    => 'testing',
                'permissions' => ['company.plugin.*'],
            ],
  1. Add a beforeSave function to the settings model and override the value attribute with something noticeable.
    public function beforeSave()
    {
        $value                     = json_decode($this->attributes['value'] ?? '[]', true);
        $value['foo']              = 'nonsense';
        $value['baz']              = 'more nonsense';
        $this->attributes['value'] = json_encode($value);
    }
  1. Open the backend settings page, change the values in the text fields, and click the save button.
  2. Check the system_settings table to verify that it accepted the values on the form and whether it accepted the changes from the beforeSave function.
  3. Modify Winter\Storm\Database\Model::bootNicerEvents to use bindEvent instead of bindEventOnce and repeat steps 4-5 again.
Workaround

It appears that beforeValidate does not exhibit this same behavior, so it can be used instead if needed/possible.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Winter\Storm\Database\Model::bootNicerEvents and compare the behavior of bindEventOnce with the reported bindEvent workaround for settings models. Reproduce the steps with a test settings model, then verify the beforeSave changes persist in the system_settings table after saving.

Written by the indexing model from the issue text.

Assessment

Tech stack
mariadb, mysql, php
Domain
backend, database
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.