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!