How to save widget fields generated from an array?

OK, so I’ve done some debugging and I know where your problem lies…

First of all in update function:

public function update( $new_instance, $old_instance ) {

    $instance = $old_instance;

    if ( empty( $this->fields ) ) {
        return $instance;
    }

    $instance = $this->save_fields( $new_instance, $old_instance );

    return $instance;
}

You shouldn’t use $old_instance as starting point. What if the list of fields is changed? Old values shouldn’t get copied to new instance. So you should use this instead:

public function update( $new_instance, $old_instance ) {

    $instance = array();

    if ( empty( $this->fields ) ) {
        return $instance;
    }

    $instance = $this->save_fields( $new_instance, $old_instance );

    return $instance;
}

But then there is one more problem in save_fields function… Again, you start with $old_instance as starting point:

public function save_fields( $new_instance, $old_instance, $parent_container = null ) {

    // Vars
    $instance = $old_instance;
    $widget_fields = $this->fields;
    if( isset( $parent_container ) ){
        $widget_fields = $widget_fields[ $parent_container ]['sub_fields'];
    }

    // Loop fields and get values to save.
    foreach ( $widget_fields as $key => $setting ) {
        $setting_type = isset( $setting['type'] ) ? $setting['type'] : '';

        // Format the value based on fields type.
        switch ( $setting_type ) {
            case 'group':
                    $group_instance = $this->save_fields( $new_instance, $old_instance, $key );
                    $instance = array_merge( $group_instance, $instance );
                break;

So if given field is a group, then:

  1. You get its fields values using recursive call.
  2. In that call you:
    1. Initiate values with old_instance.
    2. Get fields from that group and set their values.
  3. Merge group values with instance values.

So there are few problems here:

  • in 3. the values for group fields contains not only values for that group, because they were initiated with old_instance,
  • in 3. you overwrite new values from group_instance with old values from instance (which contains old values, because it is also initiated with old_instance).

So how to fix that?

It’s a really simple fix, this time. All you need to do is to change:

$instance = $old_instance;

to:

$instance = array();

in both update and save_fields functions.

It works like a charm after that change.