如何保存数组生成的Widget字段?

时间:2019-01-02 作者:Michael

我创建了一个简单的php抽象小部件类,它可以用数组中的字段生成表单。当我尝试保存字段时,所有更改都将被删除。

抽象小部件类:

<?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 );

    }

}
小部件代码:

<?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\'];
    }

}
提前非常感谢!

1 个回复
最合适的回答,由SO网友:Krzysiek Dróżdż 整理而成

好的,我已经做了一些调试,我知道你的问题在哪里。。。

首先在update 功能:

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;
}
你不应该使用$old_instance 作为起点。如果字段列表发生更改怎么办?旧值不应复制到新实例。因此,您应该使用此选项:

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;
}
但是还有一个问题save_fields 作用同样,你从$old_instance 作为起点:
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;
因此,如果给定字段是一个组,则:

您可以使用递归调用获取其字段值

第3页。组字段的值不仅包含该组的值,因为它们是用old_instance,group_instance 使用来自的旧值instance (其中包含旧值,因为它也是用old_instance).这是一个非常简单的修复,这次。您只需更改:

$instance = $old_instance;
收件人:

$instance = array();
在两者中updatesave_fields 功能。

在那次改变之后,它就像一种魅力。

相关推荐

Huge wp_options table

我有一个WP网站的问题。由于没有更多可用磁盘空间,网站崩溃。搜索时,我检测到wp\\U选项表大小为12GB,但大约只有1100行:有什么想法吗?提前感谢[UPDATE 1]如果我导出wp\\U选项表,拖放并导入,大小将减少到9,7mb:我没有机会用优化表OPTIMIZE TABLE wp_options 但如果再发生的话我会试试的[UPDATE 2]问题仍然存在。我试着OPTIMIZE TABLE wp_options;无结果: