Score:0

Remove settings no-longer present in custom modules config form from drupalSettings

rw flag

Hello Drupal Code Land,

I have created a custom admin form in a custom module as a class extending ConfigFormBase. I use this form as an admin config form to set pricing options for different variables used with a custom content type (called estimates) where the form settings are used having been passed in from drupalSettings and are used in a js file containing several custom Drupal.behaviors to calculate pricing interactively when creating a new estimate.

This all works 100% beautifully.

The problem (which isn't a huge problem) that I'm facing, is I've started going back through things cleaning up code and in a couple cases I have changed the names of several of the custom admin form fields. This appears to be causing the old named form fields with their set value to remain in drupalSettings in addition to the new form fields (modified fields) and their values.

Upon viewing the config form where I can change the form field values to update the form settings the old form fields aren't there (which they shouldn't be) so why are they still present in drupalSettings? I would have assumed they would be removed from drupalSettings once the form is resaved with any updated values, yet they are not...they remain!

I've cleared all caches in a browser on the site, and I've used drush cashe:rebuild several times which doesn't have any affect.

So I'm fairly certain I'm missing something probably in modules config form, but haven't been able to find any specific documentation regarding removal of "no-longer in existence" form field settings from drupalSettings. I've found how to override but not remove...

Here is the a snippet of the submit form function:

 public function submitForm(array &$form, FormStateInterface $form_state) {
        parent::submitForm($form, $form_state);
        $this->config('newlooks.pricesettings')
          ->set('estimate_gutter_install_base_price', $form_state->getValue('estimate_gutter_install_base_price'))

// a lot more of ->set()'s for form fields

          ->save();
 }

Thinking about it now a little more - Do I need some kind of rebuild form within the submit? Or something in the buildForm function to get drupalSettings or Drupal to remove the fields and data no-longer present in buildForm or submit functions?

Here is a snip of the buildForm function as well:

 public function buildForm(array $form, FormStateInterface $form_state) {
        $config = $this->config('newlooks.pricesettings');

        // Lots and lots of $form[estimate_something_base_price] = []
        // fields to display...

        
        return parent::buildForm($form, $form_state);
 }

I'm also trying to avoid dumping it directly from the DB which would be a quick fix (and not overly difficult) but really wouldn't solve this issue in the future as I will probably make more changes to the form over time.

Oh, and I'm currently working with Drupal 9.4.8

Thank You!

Update Edit:

using:

function newlooks_update_9101() {
  \Drupal::configFactory()->getEditable('newlooks.settings')->clear('estimate_gutter_cover_size_five');
}
  • did not clear the key in question

tried:

function newlooks_update_9102() {
  \Drupal::configFactory()->getEditable('newlooks.settings')->delete('estimate_gutter_cover_size_five');
}
  • and it deleted all the settings not the keyed one

This for my particular use case is not ideal having to run an update hook each time a configuration/settings form is edited. Seeing that if I add new form elements to the buildForm function and they become available instantly there has to be a better method.

I feel this over time could lead to a huge .module file with endless update hooks (as your not supposed to remove them once added).

Don't get me wrong hook_update_N() will work it just seams drastic.

Is there not some way to rebuild the settings or purge settings in another manor where I could actually use the existing form key:values to check against removing key:value pairs that no-longer exist?

I really feel there should be something that can handle that, another hook perhaps that runs anytime cache is cleared (just shooting in the dark on that) but something!

I definitely thank you Jaypan, I will need to use hook_update_N() else where at some point - it's just if I can avoid it in this use case it would be ideal.

Update Edit: I'll try to add a little clarity!

So in my buildForm function for example I started with this:

$form['estimate_gutter_cover_size_five'] = [
            '#type' => 'number',
            '#group' => 'cover_sizes',
            '#step' => '.01',
            '#prefix' => '<div class="gutters gtcovers gtcovers-size estimate-form-item">',
            '#title' => $this->t('5 inch Covers'),
            '#description' => $this->t('This is the base price for a standard gutter cover size <em>and is multiplied by quantity in feet for a total cost.</em>'),
            '#min' => '0.00',
            '#max' => '999.99',
            '#placeholder' => '0.00',
            '#default_value' => $config->get('estimate_gutter_cover_size_five_base_price'),
            '#suffix' => '</div>',
            '#required' => TRUE,
        ];

But later I went back and changed that to:

$form['estimate_gutter_cover_size_five_base_price'] = [
            '#type' => 'number',
            '#group' => 'cover_sizes',
            '#step' => '.01',
            '#prefix' => '<div class="gutters gtcovers gtcovers-size estimate-form-item">',
            '#title' => $this->t('5 inch Covers'),
            '#description' => $this->t('This is the base price for a standard gutter cover size <em>and is multiplied by quantity in feet for a total cost.</em>'),
            '#min' => '0.00',
            '#max' => '999.99',
            '#placeholder' => '0.00',
            '#default_value' => $config->get('estimate_gutter_cover_size_five_base_price'),
            '#suffix' => '</div>',
            '#required' => TRUE,
        ];

I open the form in a web browser and replace the placeholder with a value as it is now seen as a new element. Does exactly what I want aside from leaving garbage in the settings that aren't readily visible. Only reason I noticed it was I wanted to check variables on page load so in my js file I added: console.log(window);

While inspecting them via browser console I saw drupalSettings and figured what the heck I'll take a peak.

Upon viewing I saw both:

  • estimate_gutter_cover_size_five: "2.37"
  • estimate_gutter_cover_size_five_base_price: "2.37"

Having changed estimate_gutter_cover_size_five to estimate_gutter_cover_size_five_base_price I would have expected that estimate_gutter_cover_size_five would have been removed but was not. Considering I had to open the form for settings in a browser and set/add the new/changed elements value (where the old element and value is no longer displayed, in my mind it was "should be" gone).

Now that is just one element/field on a form with probably about 70 now and it will be growing and changing as I build everything moving forward.

That in combination with the fact I do a lot of quick testing sometimes/many times not properly naming things in reference to everything else it is or needs to be associated with, using hook_update_N() isn't going to be very helpful.

Jaypan avatar
de flag
Using `->clear()` will delete the key. So the question is why it didn't work for you. Did you run the database update script? Was your implementation of hook_update_n() executed? What was the output of the update script? And how did you determine that this did not work - after running the update script, what did you do to check this value?
Jaypan avatar
de flag
You could also just use `drush cdel` on all environments that the key needs to be deleted from, if you have access to them all. But the stable way to do it is to use hook_update_N().
Jaypan avatar
de flag
also note that `drupalSettings` refers to a JavaScript object, which has nothing to do with your code. I believe you are referring to 'active configuration'.
sidgrafix avatar
rw flag
Yes I ran the update script when attempting to use clear and it did nothing...the key was still there with value in drupalSettings. The update script ran without error nothing was reported in logs, no errors. I have access to everything drush is installed so using drush cdel shouldn't be a problem, I'm going to look that up now.. thank you
Jaypan avatar
de flag
I don't know why it wouldn't work for you, but it is how it is done, so there must have been a mistake in your code somewhere. That said, if you can use Drush, it's much quicker/easier.
sidgrafix avatar
rw flag
I tried 3 times using ->clear exactly how it is listed in my question I copied the code right from my file and pated it up there. So you got me there. Drush however I think will work ideally. Using ```drush config:edit newlooks.settings``` I can delete directly or output that to a file in tmp dir and use it to do diff compare to get a list of what needs to be removed and maybe throw a little script on it to then take that list and use ```drush config:delete key_name``` which is drush cdel. That was most helpful, thank you..
Score:1
de flag

Configuration snapshots are stored in cache, and therefore are cleared with a cache clear, but the base value is still part of the system's active configuration, and will be part of the rebuilt configuration snapshot. This is why clearing the cache isn't removing your configuration.

The way to deal fix your issue is to implement hook_update_N(), and either remove unused keys from the configuration, or if the entire configuration object needs removal, delete it.

function HOOK_update_N() {
  // Delete a key from a configuration object. For example, to 
  // delete example_module.settings.some_key:
  \Drupal::configFactory()->getEditable('example_module.settings')->clear('some_key');

  // Or, you can delete an entire config object, if none of the keys
  // are necessary any longer. For example, to delete example_module.settings:
  \Drupal::configFactory()->getEditable('example_module.settings')->delete();
}
sidgrafix avatar
rw flag
So this kinda worked. But not really friendly. Tried twice using clear and it did absolutely nothing.
Jaypan avatar
de flag
It did absolutely nothing, but kind of worked? Those seem contradictory.
sidgrafix avatar
rw flag
time expired I tried to edit my comment: ...the setting was still present. Then the 3rd time used ->delete but entered a key instead of just delete all, but it cleared them all which isn't what I wanted to do. So have to refill the entire config form again, not ideal. Plus to be honest having to add a new update hook every time a class base form is updated or changed seams a bit counterintuitive being I need the visible set form fields values to stay. I'll update my question some - but Thank You for the info
Score:0
cn flag

Instead of setting single values you could set the entire array:

public function submitForm(array &$form, FormStateInterface $form_state) {
  $data = [];
  $data['estimate_gutter_install_base_price'] = $form_state->getValue('estimate_gutter_install_base_price');

  // add more values

  $this->config('newlooks.pricesettings')
    ->setData($data)
    ->save();

  parent::submitForm($form, $form_state);
}

Or you can even try to use the form values structure if you have defined the form build accordingly:

public function submitForm(array &$form, FormStateInterface $form_state) {
    // Get all values.
    $values = $form_state->getValues();

    // Remove Form API values.
    unset(
      $values['form_build_id'],
      $values['form_token'],
      $values['form_id'],
      $values['op'],
      $values['actions']
    );

    // Save config.
    $this->config('webform_example_custom_form.settings')
      ->setData($values)
      ->save();

    // Display message.
    parent::submitForm($form, $form_state);
  }

}

Found this in the great Webform module.

sidgrafix avatar
rw flag
This sound promising...and more inline with what I'm looking for! I'm going to try this first thing when I'm back at it on Monday (packing it up for the weekend right now). Thinking the second of the 2 approaches you suggest will work with my existing buildForm and allow me to eliminate what would probably turn into an endless grow list of ->set()->save. Thank You 4uk4.. If this works I'll set both of the answers I received as correct with a depending on actual use case. Thank you both for all the help and info.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.