customization - How to save widget fields generated from an array?

admin2025-06-04  2

I created simple php abstract widget class that can generate form with fields from an array. When I try to save fields, all changes are removed.

Abstract Widget Class:

<?php
/**
 * Abstract widget class
 *
 * @class MOD_Widget
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * MOD_Widget
 *
 * @version  1.0.0
 * @extends  WP_Widget
 */
abstract class MOD_Widget extends WP_Widget {

    /**
     * CSS class.
     *
     * @var string
     */
    public $widget_cssclass;

    /**
     * Widget description.
     *
     * @var string
     */
    public $widget_description;

    /**
     * Widget ID.
     *
     * @var string
     */
    public $widget_id;

    /**
     * Widget name.
     *
     * @var string
     */
    public $widget_name;

    /**
     * Fields.
     *
     * @var array
     */
    public $fields;

    /**
     * Constructor.
     */
    public function __construct() {
        $widget_ops = array(
            'classname'   => $this->widget_cssclass,
            'description' => $this->widget_description,
            'customize_selective_refresh' => true,
        );

        parent::__construct( $this->widget_id, $this->widget_name, $widget_ops );
    }

    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;
                case 'number':
                    $instance[ $key ] = (int) $new_instance[ $key ];

                    if ( isset( $setting['min'] ) && '' !== $setting['min'] ) {
                        $instance[ $key ] = max( $instance[ $key ], $setting['min'] );
                    }

                    if ( isset( $setting['max'] ) && '' !== $setting['max'] ) {
                        $instance[ $key ] = min( $instance[ $key ], $setting['max'] );
                    }
                    break;
                case 'text':
                    $instance[ $key ] = isset( $new_instance[ $key ] ) ? sanitize_text_field( $new_instance[ $key ] ) : ( isset( $setting['default_value'] ) ? $setting['default_value'] : '' );
                    break;
                case 'textarea':
                    $instance[ $key ] = wp_kses_post( $new_instance[ $key ] );
                    break;
                case 'checkbox':
                    $instance[ $key ] = empty( $new_instance[ $key ] ) ? 0 : 1;
                    break;
                default:
                    $instance[ $key ] = isset( $new_instance[ $key ] ) ? sanitize_text_field( $new_instance[ $key ] ) : ( isset( $setting['default_value'] ) ? $setting['default_value'] : '' );
                    break;
            }
        }

        return $instance;
    }

    /**
     * Handles updating settings for the current widget instance.
     */
    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;
    }

    /**
    * Back-end widget fields
    */
    public function field_generator( $instance, $parent_container = null ) {

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

        foreach ( $widget_fields as $key => $setting ) {
            $setting_type = isset( $setting['type'] ) ? $setting['type'] : '';
            $input_css_classes = isset( $setting['class'] ) ? $setting['class'] : '';

            if( !isset( $setting_type ) ){
                return;
            }

            if( 'group' !== $setting_type ){
                $default_value = isset( $setting['default_value'] ) ? $setting['default_value'] : '';
                $value = isset( $instance[ $key ] ) ? $instance[ $key ] : $default_value;
            } else {
                $value = '';
            }

            switch ( $setting_type ) {
                case 'group':
                    ?>
                    <div id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-widget-section<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>">
                        <div class="section-header"><?php echo esc_html( $setting['label'] ); ?></div>
                        <div class="section-content">

                            <?php
                            if ( !isset( $setting['sub_fields'] ) || empty( $setting['sub_fields'] ) ) {
                                echo '<p>' . esc_html( 'This section is empty.', 'mod' ) . '</p></div></div></div>';

                                return;
                            } ?>

                            <?php $this->field_generator( $instance, $key ); ?>

                        </div>
                    </div>
                    <?php
                    break;

                case 'text':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-field-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <input id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" type="text" class="widefat<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" value="<?php echo esc_attr( $value ); ?>" />
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                case 'textarea':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-field-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <textarea id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="widefat<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" rows="4" cols="20"><?php echo esc_textarea( $value ); ?></textarea>
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                case 'number':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-field-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <input id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" type="number" class="tiny-text<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" step="<?php echo esc_attr( $setting['step'] ); ?>" min="<?php echo esc_attr( $setting['min'] ); ?>" <?php if ( ! empty( $setting['max'] ) ) : ?>max="<?php echo esc_attr( $setting['max'] ); ?>"<?php endif; ?> value="<?php echo esc_attr( $value ); ?>" />
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                case 'checkbox':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <input id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" type="checkbox" class="checkbox<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" <?php echo checked( $value, 1 ); ?> />
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-checkbox-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                // Default: run an action.
                default:
                    do_action( 'mod_widget_field_' . $setting_type, $key, $value, $setting, $instance );
                    break;

            }
        }
    }

    /**
     * Outputs the settings form for the widget.
     */
    public function form( $instance ) {

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

        $this->field_generator( $instance );

    }

}

Widget code:

<?php
/**
 * Widget - 
 *
 * @version 1.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Widget - Test.
 */
class MOD_Widget_Test extends MOD_Widget {

    /**
     * Constructor.
     */
    public function __construct() {
        $this->widget_cssclass = 'mod mod_widget_test_abstract';
        $this->widget_description = __( 'A widget description.', 'mod' );
        $this->widget_id = 'mod-widget-test';
        $this->widget_name = __( 'MOD Widget - Test', 'mod' );
        $this->fields = array(

            'base_title' => array(
                'label' => __( 'Base Title', 'mod' ),
                'type' => 'text',
                'class' => '',
                'default_value' => 'Test',
            ),
            'about_title' => array(
                'label' => __( 'About Title', 'mod' ),
                'type' => 'text',
                'class' => '',
                'default_value' => '',
            ),
            'base_num' => array(
                'label' => __( 'Base Number', 'mod' ),
                'type' => 'number',
                'class' => '',
                'default_value' => 10,
                'min' => -1,
                'step' => 1,
            ),
            'base_checkbox' => array(
                'label' => __( 'Base Checkbox', 'mod' ),
                'type' => 'checkbox',
                'class' => '',
                'default_value' => 0,
            ),

            // General
            'group_general' => array(
                'label' => __( 'General', 'mod' ),
                'type' => 'group',
                'sub_fields' => array(
                    'general_title' => array(
                        'label' => __( 'General Title', 'mod' ),
                        'type' => 'text',
                        'class' => '',
                        'default_value' => '',
                    ),
                    'general_num' => array(
                        'label' => __( 'General Number', 'mod' ),
                        'type' => 'number',
                        'class' => '',
                        'default_value' => '',
                        'min' => -1,
                        'step' => 1,
                    ),
                    'general_checkbox' => array(
                        'label' => __( 'General Checkbox', 'mod' ),
                        'type' => 'checkbox',
                        'class' => '',
                        'default_value' => 1,
                    ),
                ),
            ),

            // Special
            'group_special' => array(
                'label' => __( 'Special Parameters', 'mod' ),
                'type' => 'group',
                'sub_fields' => array(
                    'special_title' => array(
                        'label' => __( 'Special Title', 'mod' ),
                        'type' => 'text',
                        'class' => '',
                        'default_value' => '',
                    ),
                    'special_desc' => array(
                        'label' => __( 'Special Description', 'mod' ),
                        'type' => 'textarea',
                        'class' => '',
                        'default_value' => '',
                    ),
                ),
            ),
        );

        parent::__construct();
    }

    /**
     * Output widget.
     *
     * @see WP_Widget
     *
     * @param array $args     Arguments.
     * @param array $instance Widget instance.
     */
    public function widget( $args, $instance ) {
        ob_start();

        echo $args['before_widget'];

        echo '<pre>';
        var_dump( $instance );
        echo '</pre>';

        echo $args['after_widget'];
    }

}

Thank you so much in advance!

I created simple php abstract widget class that can generate form with fields from an array. When I try to save fields, all changes are removed.

Abstract Widget Class:

<?php
/**
 * Abstract widget class
 *
 * @class MOD_Widget
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * MOD_Widget
 *
 * @version  1.0.0
 * @extends  WP_Widget
 */
abstract class MOD_Widget extends WP_Widget {

    /**
     * CSS class.
     *
     * @var string
     */
    public $widget_cssclass;

    /**
     * Widget description.
     *
     * @var string
     */
    public $widget_description;

    /**
     * Widget ID.
     *
     * @var string
     */
    public $widget_id;

    /**
     * Widget name.
     *
     * @var string
     */
    public $widget_name;

    /**
     * Fields.
     *
     * @var array
     */
    public $fields;

    /**
     * Constructor.
     */
    public function __construct() {
        $widget_ops = array(
            'classname'   => $this->widget_cssclass,
            'description' => $this->widget_description,
            'customize_selective_refresh' => true,
        );

        parent::__construct( $this->widget_id, $this->widget_name, $widget_ops );
    }

    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;
                case 'number':
                    $instance[ $key ] = (int) $new_instance[ $key ];

                    if ( isset( $setting['min'] ) && '' !== $setting['min'] ) {
                        $instance[ $key ] = max( $instance[ $key ], $setting['min'] );
                    }

                    if ( isset( $setting['max'] ) && '' !== $setting['max'] ) {
                        $instance[ $key ] = min( $instance[ $key ], $setting['max'] );
                    }
                    break;
                case 'text':
                    $instance[ $key ] = isset( $new_instance[ $key ] ) ? sanitize_text_field( $new_instance[ $key ] ) : ( isset( $setting['default_value'] ) ? $setting['default_value'] : '' );
                    break;
                case 'textarea':
                    $instance[ $key ] = wp_kses_post( $new_instance[ $key ] );
                    break;
                case 'checkbox':
                    $instance[ $key ] = empty( $new_instance[ $key ] ) ? 0 : 1;
                    break;
                default:
                    $instance[ $key ] = isset( $new_instance[ $key ] ) ? sanitize_text_field( $new_instance[ $key ] ) : ( isset( $setting['default_value'] ) ? $setting['default_value'] : '' );
                    break;
            }
        }

        return $instance;
    }

    /**
     * Handles updating settings for the current widget instance.
     */
    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;
    }

    /**
    * Back-end widget fields
    */
    public function field_generator( $instance, $parent_container = null ) {

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

        foreach ( $widget_fields as $key => $setting ) {
            $setting_type = isset( $setting['type'] ) ? $setting['type'] : '';
            $input_css_classes = isset( $setting['class'] ) ? $setting['class'] : '';

            if( !isset( $setting_type ) ){
                return;
            }

            if( 'group' !== $setting_type ){
                $default_value = isset( $setting['default_value'] ) ? $setting['default_value'] : '';
                $value = isset( $instance[ $key ] ) ? $instance[ $key ] : $default_value;
            } else {
                $value = '';
            }

            switch ( $setting_type ) {
                case 'group':
                    ?>
                    <div id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-widget-section<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>">
                        <div class="section-header"><?php echo esc_html( $setting['label'] ); ?></div>
                        <div class="section-content">

                            <?php
                            if ( !isset( $setting['sub_fields'] ) || empty( $setting['sub_fields'] ) ) {
                                echo '<p>' . esc_html( 'This section is empty.', 'mod' ) . '</p></div></div></div>';

                                return;
                            } ?>

                            <?php $this->field_generator( $instance, $key ); ?>

                        </div>
                    </div>
                    <?php
                    break;

                case 'text':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-field-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <input id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" type="text" class="widefat<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" value="<?php echo esc_attr( $value ); ?>" />
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                case 'textarea':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-field-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <textarea id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="widefat<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" rows="4" cols="20"><?php echo esc_textarea( $value ); ?></textarea>
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                case 'number':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-field-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <input id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" type="number" class="tiny-text<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" step="<?php echo esc_attr( $setting['step'] ); ?>" min="<?php echo esc_attr( $setting['min'] ); ?>" <?php if ( ! empty( $setting['max'] ) ) : ?>max="<?php echo esc_attr( $setting['max'] ); ?>"<?php endif; ?> value="<?php echo esc_attr( $value ); ?>" />
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                case 'checkbox':
                    ?>
                    <div class="mod-widget-option mod-option-type-<?php echo esc_attr( $setting['type'] ); ?>">
                        <input id="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" type="checkbox" class="checkbox<?php if ( ! empty( $input_css_classes ) ) echo ' ' . implode( ' ', $input_css_classes ); ?>" name="<?php echo esc_attr( $this->get_field_name( $key ) ); ?>" <?php echo checked( $value, 1 ); ?> />
                        <label for="<?php echo esc_attr( $this->get_field_id( $key ) ); ?>" class="mod-checkbox-label"><?php echo esc_html( $setting['label'] ); ?></label>
                        <?php if ( ! empty( $setting['desc'] ) ) : ?>
                            <p class="small-desc"><?php echo esc_html( $setting['desc'] ); ?></p>
                        <?php endif; ?>
                    </div>
                    <?php
                    break;

                // Default: run an action.
                default:
                    do_action( 'mod_widget_field_' . $setting_type, $key, $value, $setting, $instance );
                    break;

            }
        }
    }

    /**
     * Outputs the settings form for the widget.
     */
    public function form( $instance ) {

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

        $this->field_generator( $instance );

    }

}

Widget code:

<?php
/**
 * Widget - 
 *
 * @version 1.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Widget - Test.
 */
class MOD_Widget_Test extends MOD_Widget {

    /**
     * Constructor.
     */
    public function __construct() {
        $this->widget_cssclass = 'mod mod_widget_test_abstract';
        $this->widget_description = __( 'A widget description.', 'mod' );
        $this->widget_id = 'mod-widget-test';
        $this->widget_name = __( 'MOD Widget - Test', 'mod' );
        $this->fields = array(

            'base_title' => array(
                'label' => __( 'Base Title', 'mod' ),
                'type' => 'text',
                'class' => '',
                'default_value' => 'Test',
            ),
            'about_title' => array(
                'label' => __( 'About Title', 'mod' ),
                'type' => 'text',
                'class' => '',
                'default_value' => '',
            ),
            'base_num' => array(
                'label' => __( 'Base Number', 'mod' ),
                'type' => 'number',
                'class' => '',
                'default_value' => 10,
                'min' => -1,
                'step' => 1,
            ),
            'base_checkbox' => array(
                'label' => __( 'Base Checkbox', 'mod' ),
                'type' => 'checkbox',
                'class' => '',
                'default_value' => 0,
            ),

            // General
            'group_general' => array(
                'label' => __( 'General', 'mod' ),
                'type' => 'group',
                'sub_fields' => array(
                    'general_title' => array(
                        'label' => __( 'General Title', 'mod' ),
                        'type' => 'text',
                        'class' => '',
                        'default_value' => '',
                    ),
                    'general_num' => array(
                        'label' => __( 'General Number', 'mod' ),
                        'type' => 'number',
                        'class' => '',
                        'default_value' => '',
                        'min' => -1,
                        'step' => 1,
                    ),
                    'general_checkbox' => array(
                        'label' => __( 'General Checkbox', 'mod' ),
                        'type' => 'checkbox',
                        'class' => '',
                        'default_value' => 1,
                    ),
                ),
            ),

            // Special
            'group_special' => array(
                'label' => __( 'Special Parameters', 'mod' ),
                'type' => 'group',
                'sub_fields' => array(
                    'special_title' => array(
                        'label' => __( 'Special Title', 'mod' ),
                        'type' => 'text',
                        'class' => '',
                        'default_value' => '',
                    ),
                    'special_desc' => array(
                        'label' => __( 'Special Description', 'mod' ),
                        'type' => 'textarea',
                        'class' => '',
                        'default_value' => '',
                    ),
                ),
            ),
        );

        parent::__construct();
    }

    /**
     * Output widget.
     *
     * @see WP_Widget
     *
     * @param array $args     Arguments.
     * @param array $instance Widget instance.
     */
    public function widget( $args, $instance ) {
        ob_start();

        echo $args['before_widget'];

        echo '<pre>';
        var_dump( $instance );
        echo '</pre>';

        echo $args['after_widget'];
    }

}

Thank you so much in advance!

Share Improve this question edited Jan 4, 2019 at 22:24 Michael asked Jan 2, 2019 at 17:23 MichaelMichael 1821 silver badge11 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1 +50

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.

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1749036685a315819.html

最新回复(0)