Properly delete multiselect settings in WordPress
Published: â Leave a comment
Working with settings in WordPress is usually pretty straight forward with the settings API. However, if youâre using multiselect fields, there is a problem. Since the selection on a multiselect field can be completely empty, it makes deleting the last value impossible by default.
But why is that? If you select at least one value of a multiselect field (a <select>
with a multiple
attribute), this value (or list of values) will be part of $_POST
by submitting the setting and thus will be updated in the database by the settings API.
However, if you deselect all values of the multiselect field afterwards and submit, there is no entry in $_POST
(since empty fields are omitted) and thus the field is left as is by WordPress, since it doesnât know that this field should be changed.
Unfortunately, there is no easy filter to tell WordPress which fields to check for changed values, and thus we need to trick a little bit here. The only filter that runs in this state is allowed_options
, checking for whether the current given $_POST
fields are allowed to be updated. So I (mis-)use this filter and delete the option if it is registered via register_setting
, but is not part of the $_POST
fields:
/**
* Delete all empty settings.
*
* @param array $allowed_options List of allowed options
* @return array List of allowed options
*/
static function delete_empty_settings( array $allowed_options ): array {
global $wp_settings_fields;
// phpcs:disable WordPress.Security.NonceVerification.Missing
if (
! isset( $_POST['option_page'] )
|| ! isset( $_POST['action'] )
|| \sanitize_text_field( \wp_unslash( $_POST['option_page'] ) ) !== 'product_settings'
|| \sanitize_text_field( \wp_unslash( $_POST['action'] ) ) !== 'update'
) {
return $allowed_options;
}
foreach ( $wp_settings_fields['product_settings'] as $settings_fields ) {
foreach ( \array_keys( $settings_fields ) as $field_name ) {
if ( ! isset( $_POST[ $field_name ] ) ) {
\delete_option( $field_name );
}
}
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
return $allowed_options;
}
Code language: PHP (php)
In line 14, I check whether my custom option page is the one the request comes from. This needs to be adjusted according to your settings page. The same applies to the key in the global $wp_settings_fields
in line 20.
The code then iterates through all fields and checks whether they are part of the $_POST
fields. If not, they will be deleted.
The $allowed_options
always stay untouched. The filter is just used to be able to use the settings update request to run the code.
By the way: the ignored nonce verification is on purpose. The filter only runs after a successful nonce check in /wp-admin/options.php
and multiple nonce verifications are not supported by WordPress.