Question

I've been trying to use the Settings API for the first time. I managed to piece together a working example but I'm stuck on how to make multiple input fields required. Right now I have an admin notice displaying but it displays for each field, so if I have 3 fields it displays 3 times. How do I make this work with multiple fields?

Register the fields...

function my_settings_init() {
    
    add_settings_section(
        'my_custom_setting_section',
        'Text for the page',
        'my_custom_setting_section_callback',
        'my-settings'
    );

        add_settings_field(
            'field-1',
            'Field 1',
            'my_settings_step1_markup',
            'my-settings',
            'my_custom_setting_section'
        );

        register_setting('my-settings', 'field-1', 'my_settings_settings_validation');
    
        add_settings_field(
            'field-2',
            '<span class="my-settings-step">Step 2</span>',
            'my_settings_step2_markup',
            'my-settings',
            'my_custom_setting_section'
        );

        register_setting('my-settings', 'field-2', 'my_settings_settings_validation');
    
        ... More fields below
}

add_action('admin_init', 'my_settings_init');

My validation callback so far...

function my_settings_settings_validation($input){
    
    $message = null;
    $type = null;
    
    if (null != $input) {
        if (false === get_option('my-settings')) {
            add_option('my-settings', $input);
            $message = __('Option added!');
            $type = 'updated';
        } else {
            update_option('my-settings', $input);
            $message = __('Option updated!');
            $type = 'updated';
        }
    }else{
        $message = __('There was a problem.');
        $type = 'error';
    }
    
    add_settings_error('my_option_notice', 'my_option_notice', $message, $type);
    
    return $input;
}

Any help would be great as I am clearly missing something!

Was it helpful?

Solution

If you want your sanitization callback to apply only to a specific settings field (i.e. a database option registered using register_setting()), then you would want to use a different callback for each of your settings fields. E.g.

// Sample field HTML:
//<input name="field-1" value="<?php esc_attr( get_option( 'field-1' ) ); ?>">

function my_settings_field_1_validation( $value ) {
    if ( empty( $value ) ) {
        $value = get_option( 'field-1' ); // ignore the user's changes and use the old database value
        add_settings_error( 'my_option_notice', 'invalid_field_1', 'Field 1 is required.' );
    }

    return $value;
}

function my_settings_field_2_validation( $value ) {
    if ( empty( $value ) ) {
        $value = get_option( 'field-2' ); // ignore the user's changes and use the old database value
        add_settings_error( 'my_option_notice', 'invalid_field_2', 'Field 2 is required.' );
    }

    return $value;
}

// Then in your my_settings_init():
register_setting( 'my-settings', 'field-1', 'my_settings_field_1_validation' );
register_setting( 'my-settings', 'field-2', 'my_settings_field_2_validation' );

Or you can use a closure with register_setting():

function my_settings_field_validation( $value, $field, $name ) {
    if ( empty( $value ) ) {
        $value = get_option( $field ); // ignore the user's changes and use the old database value
        add_settings_error( 'my_option_notice', "invalid_$field", "$name is required." );
    }

    return $value;
}

// Then in your my_settings_init():

register_setting( 'my-settings', 'field-1', function ( $value ) {
    return my_settings_field_validation( $value, 'field-1', 'Field 1' );
} );

register_setting( 'my-settings', 'field-2', function ( $value ) {
    return my_settings_field_validation( $value, 'field-2', 'Field 2' );
} );

And why do you call add_option() and update_option() in your callback (my_settings_settings_validation())? I mean, register_setting() is intended for registering an option that will be automatically added/updated whenever the value is changed via your settings form, so you shouldn't need to update the value manually.

And if you actually wanted those settings be part of a single database option named my-settings, then you would want to save the option an array, then call register_setting() just once and use my-settings[<field>] in your input's name attribute. E.g.

// Sample field HTML:
//<input name="my-settings[field-1]" value="<?php echo esc_attr( $options['field-1'] ); ?>">
// whereby $options is:
//$options = (array) get_option( 'my-settings' );

function my_settings_field_validation( $value ) {
    $value = (array) $value;
    $valid = true;

    if ( empty( $value['field-1'] ) ) {
        $valid = false;
        add_settings_error( 'my_option_notice', 'invalid_field-1', 'Field 1 is required.' );
    }

    if ( empty( $value['field-2'] ) ) {
        $valid = false;
        add_settings_error( 'my_option_notice', 'invalid_field-2', 'Field 2 is required.' );
    }

    // Ignore the user's changes and use the old database value.
    if ( ! $valid ) {
        $value = get_option( 'my-settings' );
    }

    return $value;
}

// Then in your my_settings_init():
register_setting( 'my-settings', 'my-settings', 'my_settings_field_validation' );

BTW, you could simply add required to your field HTML, but it's always a good idea to validate both on the client-side (browser) and server-side.

Also, if I were you, I'd probably use the plural "notices" as in my_option_notices.. but that's just my own preference. =)

Licensed under: CC-BY-SA with attribution
Not affiliated with wordpress.stackexchange
scroll top