register_setting & add_settings_error validation issues with multiple fields
-
22-04-2021 - |
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!
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. =)