A repeatable field with multiple value for wordpress theme option

I am adding a new ticker option for my theme. The problem is the data is not saving. I am not sure where I mess my code. enter image description here

<?php 
    
class SpecialProductTicker {
    private $special_product_ticker_options;

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'special_product_ticker_add_plugin_page' ) );
        add_action( 'admin_init', array( $this, 'special_product_ticker_page_init' ) );
    }

    public function special_product_ticker_add_plugin_page() {
        add_submenu_page(
            'exch-settings',
            'Special Product', // page_title
            'Special Product', // menu_title
            'manage_options', // capability
            'special-product-ticker', // menu_slug
            array( $this, 'special_product_ticker_create_admin_page' ) // function
        );
    }
    

    public function special_product_ticker_create_admin_page() {
        $this->special_product_ticker_options = get_option( 'special_product_ticker_option_name' ); ?>

        <div class="wrap" style="background: #fff;padding: 27px 50px;">
            <h2>Special Product</h2>
            
            <?php settings_errors(); ?>

            <form method="post" action="options.php">
                <?php
                    settings_fields( 'special_product_ticker_option_group' );
                    do_settings_sections( 'special-product-ticker-admin' );
                    submit_button();
                ?>
            </form>
        </div>
    <?php }

    public function special_product_ticker_page_init() {
        register_setting(
            'special_product_ticker_option_group', // option_group
            'special_product_ticker_option_name', // option_name
            array( $this, 'special_product_ticker_sanitize' ) // sanitize_callback
        );

        add_settings_section(
            'special_product_ticker_setting_section', // id
            'Settings', // title
            array( $this, 'special_product_ticker_section_info' ), // callback
            'special-product-ticker-admin' // page
        );

        add_settings_field(
            'special_ticker_text', // id
            'Ticker text & Color', // title
            array( $this, 'special_ticker_text_callback' ), // callback
            'special-product-ticker-admin', // page
            'special_product_ticker_setting_section' // section
        );
        
    }

    public function special_product_ticker_sanitize($input) {
        $sanitary_values = array();
        if ( isset( $input['special_ticker_text'] ) ) {
            $sanitary_values['special_ticker_text'] = sanitize_text_field( $input['special_ticker_text'] );
        }
        
        if ( isset( $input['special_ticker_color'] ) ) {
            $sanitary_values['special_ticker_color'] = sanitize_text_field( $input['special_ticker_color'] );
        }

        return $sanitary_values;
    }

    public function special_product_ticker_section_info() {
        echo'
        <style>
        .ticker-input-group {
            display: inline-flex;
        }
        .remove {
            color: #f00;
            cursor: pointer;
        }
        
        button.add {
            background: #00c008;
            border: 0;
            padding: 3px 10px 5px;
            cursor: pointer;
            border-radius: 4px;
            color: #fff;
        }
        </style>
        ';
        
    }

    
    public function special_ticker_text_callback() {
        
        //get the saved meta as an array
        $tickers = $this->special_product_ticker_options;
        //var_dump($tickers);
        $c = 0;
        if(is_array($tickers)){
            foreach( $tickers as $key => $ticker ) {
                if ( isset( $ticker['special_ticker_text'] ) | isset( $ticker['special_ticker_color'] )) {
                    printf( '<p>Title <input type="text" name="special_product_ticker_option_name[%1$s][special_ticker_text]" value="%2$s" /> - Color <input class="thebomb-color-picker" type="text" name="special_product_ticker_option_name[%1$s][special_ticker_color]" value="%3$s" /><span class="remove">%5$s</span></p>', $c, $ticker['title'], $ticker['color_code'], __( ' -Remove' ) );
                    $c++;
                }
            }
            
        }
        
        if(empty($tickers)){
            printf( '<p>Title <input type="text" name="special_product_ticker_option_name[%1$s][special_ticker_text]" /> - Color <input class="thebomb-color-picker" type="text" name="special_product_ticker_option_name[%1$s][special_ticker_color]" /><span class="remove">%2$s</span></p>', $c, __( ' -Remove' ) );
        }
        ?>
        <span id="FieldHere"></span>
        <button class="add"><?php _e(' + Add New'); ?></button>
        <script>
        var $ =jQuery.noConflict();
        $(document).ready(function() {
            var count = <?php echo $c; ?>;
            $(".add").click(function() {
                count++;
                $('#FieldHere').append('<p>Title <input type="text" name="special_product_ticker_option_name['+count+'][special_ticker_text]" value="" /> - Color <input class="thebomb-color-picker" type="text" name="polls['+count+'][special_ticker_color]" value="" /><span class="remove"> -Remove</span></p>' );
               // turn the cloned field into a colorpicker
               $('.thebomb-color-picker').wpColorPicker();
               return false;
            });
            
            $(".remove").live('click', function() {
                $(this).parent().remove();
            });
            
            //Color picker 
            $('.thebomb-color-picker').wpColorPicker();
        });
        </script>
        <?php
        
    }
}
if ( is_admin() )
    $special_product_ticker = new SpecialProductTicker(); 

Answer

There were a few issues you had going on here.

  1. You had your sanitize filter checking the input as a string, when it is actually an array.
  2. You called the sanitize_filter using the older method. See https://developer.wordpress.org/reference/functions/register_setting/
  3. Some of the jQuery is using deprecated shorthands.
  4. in special_ticker_text_callback the printf had the wrong number of variables. The final one was labeled %5$s when there were only 4 listed.
class SpecialProductTicker {
    private $special_product_ticker_options;

    public function __construct() {
        add_action( 'admin_menu', array( $this, 'special_product_ticker_add_plugin_page' ) );
        add_action( 'admin_init', array( $this, 'special_product_ticker_page_init' ) );
    }

    public function special_product_ticker_add_plugin_page() {
        add_submenu_page(
            'exch-settings',
            'Special Product', // page_title
            'Special Product', // menu_title
            'manage_options', // capability
            'special-product-ticker', // menu_slug
            array( $this, 'special_product_ticker_create_admin_page' ) // function
        );
    }


    public function special_product_ticker_create_admin_page() {
        $this->special_product_ticker_options = get_option( 'special_product_ticker_option_name' ); ?>

        <div class="wrap" style="background: #fff;padding: 27px 50px;">
            <h2>Special Product</h2>

            <?php settings_errors(); ?>

            <form method="post" action="options.php">
                <?php
                settings_fields( 'special_product_ticker_option_group' );
                do_settings_sections( 'special-product-ticker-admin' );
                submit_button();
                ?>
            </form>
        </div>
    <?php }

    public function special_product_ticker_page_init() {
        register_setting(
            'special_product_ticker_option_group', // option_group
            'special_product_ticker_option_name', // option_name
            array(
                'type' => 'array',
                'sanitize_callback' => array($this, 'special_product_ticker_sanitize'),
                'default' => NULL
            ) // sanitize_callback
        );

        add_settings_section(
            'special_product_ticker_setting_section', // id
            'Settings', // title
            array( $this, 'special_product_ticker_section_info' ), // callback
            'special-product-ticker-admin' // page
        );

        add_settings_field(
            'special_ticker_text', // id
            'Ticker text & Color', // title
            array( $this, 'special_ticker_text_callback' ), // callback
            'special-product-ticker-admin', // page
            'special_product_ticker_setting_section' // section,
        );

    }

    public function special_product_ticker_sanitize($input_array) {
        $sanitary_values = array();
        foreach ($input_array as $key => $input){
            if (isset($input['special_ticker_text'])) {
                $sanitary_values[$key]['special_ticker_text'] = sanitize_text_field($input['special_ticker_text']);
            }

            if (isset($input['special_ticker_color'])) {
                $sanitary_values[$key]['special_ticker_color'] = sanitize_text_field($input['special_ticker_color']);
            }
        }

        return $sanitary_values;
    }

    public function special_product_ticker_section_info() {
        echo'
        <style>
        .ticker-input-group {
            display: inline-flex;
        }
        .remove {
            color: #f00;
            cursor: pointer;
        }
        
        button.add {
            background: #00c008;
            border: 0;
            padding: 3px 10px 5px;
            cursor: pointer;
            border-radius: 4px;
            color: #fff;
        }
        </style>
        ';

    }


    public function special_ticker_text_callback() {
        wp_enqueue_style( 'wp-color-picker' );
        wp_enqueue_script('wp-color-picker');
        //get the saved meta as an array
        $tickers = get_option('special_product_ticker_option_name');
        $c = 0;
        if(is_array($tickers)){
            foreach( $tickers as $key => $ticker ) {
                if ( isset( $ticker['special_ticker_text'] ) | isset( $ticker['special_ticker_color'] )) {
                    printf( '<p>Title <input type="text" name="special_product_ticker_option_name[%1$s][special_ticker_text]" value="%2$s" /> - Color <input class="thebomb-color-picker" type="text" name="special_product_ticker_option_name[%1$s][special_ticker_color]" value="%3$s" /><span class="remove">%4$s</span></p>', $c, $ticker['special_ticker_text'], $ticker['special_ticker_color'], __( ' -Remove' ) );
                    $c++;
                }
            }

        }

        if(empty($tickers)){
            printf( '<p>Title <input type="text" name="special_product_ticker_option_name[%1$s][special_ticker_text]" /> - Color <input class="thebomb-color-picker" type="text" name="special_product_ticker_option_name[%1$s][special_ticker_color]" /><span class="remove">%2$s</span></p>', $c, __( ' -Remove' ) );
        }
        ?>
        <span id="FieldHere"></span>
        <a class="add button"><?php _e(' + Add New'); ?></a>
        <script>
            var $ =jQuery.noConflict();
            $(function() {
                let count = <?php echo $c; ?>;
                $(".add").on('click', function() {
                    count++;
                    $('#FieldHere').append('<p>Title <input type="text" name="special_product_ticker_option_name['+count+'][special_ticker_text]" value="" /> - Color <input class="thebomb-color-picker" type="text" name="special_product_ticker_option_name['+count+'][special_ticker_color]" value="" /><span class="remove"> -Remove</span></p>' );
                    // turn the cloned field into a colorpicker
                    $('.thebomb-color-picker').wpColorPicker();
                    return false;
                });

                $(".remove").on('click', function() {
                    $(this).parent().remove();
                });

                //Color picker
                $('.thebomb-color-picker').wpColorPicker();
            });
        </script>
        <?php

    }
}
if ( is_admin() )
    $special_product_ticker = new SpecialProductTicker();

Leave a Reply

Your email address will not be published. Required fields are marked *