[2.0.0-alpha2] Fix default value management in sources

Created on 31 March 2024, 8 months ago
Updated 6 June 2024, 6 months ago

Problem/Motivation

In source plugins, #default_value are calling PluginSettingsInterface::getSetting() in PluginSettingsInterface::settingsForm().

Example:

    $build['value'] = [
      '#type' => 'textfield',
      '#default_value' => $this->getSetting('value'),
    ];

More complicated example:

    $build["menu"] = [
      '#type' => 'select',
      '#title' => $this->t("Menu"),
      '#options' => $this->getMenuList(),
      '#default_value' => $this->getSetting('menu'),
    ];
    $options = range(0, $this->menuLinkTree->maxDepth());
    unset($options[0]);
    $build['level'] = [
      '#type' => 'select',
      '#title' => $this->t('Initial visibility level'),
      '#default_value' => $this->getSetting('level'),
      '#options' => $options,
    ];

getSetting() must retrieve value already saved in the form element in a previous submission, but if this value is missing or null (but not empty or false), a default value must be retrieved instead.

However, this is a mess:

  • Some sources plugins are defining default values from settingsForm() method: CheckboxesWidget.php: '#default_value' => $this->getSetting('value') ?? [],
  • Some sources plugins (MenuSource & ) are defining default values implementing PluginSettingsInterface::defaultSettings() method, but this method is already used in PropTypePluginBase and it is not working.

Also, some default value are defined in the prop definition itself, using JSON schema:

    string_default:
      title: "String with default"
      type: "string"
      default: "Default text"
    string_enum_default:
      title: "String with enum with default (bottom)"
      type: "string"
      enum:
        - top
        - bottom
      default: bottom   

Careful about those ones: there is not always an exact mapping between values stored by the form and values expected by the prop, that's why we sometimes have logic in SourceInterface::getPropValue() implementations.

Proposed resolution

Let's discuss ;)

API changes

We are not expecting breaking changes.

🐛 Bug report
Status

Fixed

Version

2.0

Component

Code

Created by

🇫🇷France pdureau Paris

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @pdureau
  • 🇫🇷France just_like_good_vibes PARIS

    Pierre, just to be sure, would you please elaborate what you exactly meant when you wrote this :
    "Some sources plugins (MenuSource & ) are defining default values implementing PluginSettingsInterface::defaultSettings() method, but this method is already used in PropTypePluginBase and it is not working."

    thank you :)

  • 🇫🇷France pdureau Paris

    so, we need to load default only on first 'load" of the form (values are not set yet). We don't need to already submited empty/false value with default value.

    It would be nice to make PluginSettingsInterface::getSetting() the sole manager of this default value strategy:

    1. if there is a default value in the prop JSON schema definition, take it
    2. else, if tehre is a defaulty value in source plugin's PluginSettingsInterface::defaultSettings() , take it
    3. else, do nothing
  • Status changed to Needs review 6 months ago
  • 🇫🇷France just_like_good_vibes PARIS

    hello,
    i proposed a new code to handle this issue.
    please let's review it.

    ---

    By the way, when I checked the code from Drupal\ui_patterns\SourcePluginBase and SourceInterface,
    i remarked some functions are unused now, and i would like to open the way to remove them.
    I have the impression they are declared just to have a complete set of get/set helpers for Setting and Settings, but why keep them ?
    here are those methods : "getSettings()" and "setSetting(string $key, mixed $value)"
    since we have like some code dealing with defaultSettings happening in getSettings and setSettings, it would be better to remove the two others.
    what do you think? should we? if yes, we remove them in this issue, in another issue?

    thank you

  • Status changed to Needs work 6 months ago
  • 🇫🇷France pdureau Paris

    Analysis: https://docs.google.com/spreadsheets/d/1lyLP5Xl6b5zgp0-f9VmNPJjFNTTEICZ7...

    The current (before your MR) SourcePluginBase::defaultSettings() looks like that:

      public function defaultSettings(): array {
        return ['value' => $this->propDefinition['default'] ?? NULL];
      }

    According to the analysis sheet, it works for those source which have a value setting and this value setting are valid according to the prop definition schema:

    • checkbox
    • checkboxes
    • color
    • number
    • select
    • textfield
    • url

    We can add in this SourcePluginBase::defaultSettings() a condition excluding the slots which never have default value, so we cover 2 more sources:

    • block
    • wysiwyg

    So, those sources plugins must override defaultSettings():

    • menu
    • breadcrumb
    • url
    • attributes
    • list_textarea
  • Status changed to Needs review 6 months ago
  • 🇫🇷France just_like_good_vibes PARIS

    hello,
    based on the previous discussions, i proposed a small rework of the source plugins to better handle default values while at the same time
    providing a little clarity about the different sources available.
    i felt like i had to introduce a special base class for the simple source plugins that shares some behavior about their value stored in the settings and compatible with the default from prop schema.
    I also finally removed the two functions as mentioned earlier in the comments, to simplify the already too large source plugin classes.
    (and sorry for the mess above this comment in the commit history of the MR, rebase did not go as expected).
    let me know if you find it better.

  • Issue was unassigned.
  • Status changed to Fixed 6 months ago
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024