Using update_option & get_option Instead of the Complicated Settings API in WordPress Plugin Development

IMPORTANT EDIT: Security is vital, and I’ve been called out by two very respectable WordPress engineers on Advanced WordPress for my lack of security in this post and that using the Settings API is important because it handles this security for you.  I’m adding nonce, capability checks and proper sanitization to this code right now and should be in place shortly.

The code in here is also written for brevity of example.  Copying and pasting will work but it’s best practice to put functionality in the appropriate places.  For example instead of just using sanitize( $_POST[‘var’] ) you should add a filter that’s global.


WOAH that’s a long title.  This isn’t going to be a crazy long post but all those items mentioned are very important to the content.  The WordPress Settings API is powerful and for large plugins or themes it might be the best way to go about managing a large array of settings and options.  But most of my plugins are small scale and I find it overly complicated and bulky.

My Experience With The WP Settings API

If you don’t care about my experience and just want to see my solution click here.

My first plugin where I needed a core admin page just for settings was the WP Name Your Donation plugin.  I’ve all but abandoned that plugin because there are so many awesome plugins out there that cover its functionality and more.  It was a great learning experience building it though.

It allows multiple gateway options for accepting user determined amounts of money, i.e. donations.  So the admin page needed to allow a user to set the api keys for multiple gateways and I was starting off with Stripe, Authorize.Net and PayPal.

Googling admin settings in plugin development while I was building this, which was only my second plugin, referred me to articles on the WP Settings API.  It took a TON of trial and error to get this functioning and I still don’t think it’s implemented right.  Take a look at this:

https://gist.github.com/ChrisFlannagan/5ed116c35b97b601fc6d26dcd7085bca

I start by building out all my options in arrays.  These arrays hold all the info for each setting input I will need for each payment method option.  Then I loop through them all using the Settings API method add_settings_field() after using the methods register_setting() and add_settings_section(), note this is in the admin_init hook:

https://gist.github.com/ChrisFlannagan/1cabe324b70ae0d18921d5277574e62a

I have different functions for different kinds of fields which you can see in the parameters of add_settings_field.

Codex Confusion

Over in the codex for the Settings API you will notice multiple times they have small callout notes saying “make sure this was done first.” The confusion is real because my code does not follow a lot of the directions yet it works.  But when I was playing with it last night in a new plugin I was following directions exactly and it wouldn’t work.  For example:

NOTE: You MUST register any options used by this function with register_setting() or they won’t be saved and updated automatically.

In my code you will see I use the register_setting method, but I use it with the parameters

( self::PLUGIN_ABBR . '-group', self::PLUGIN_ABBR . '-group' )

The PLUGIN_ABBR is just wpnyd so the string “wpnyd-group” is set for both parameters and here’s the kicker: I DON’T USE THAT STRING ANYWHERE ELSE IN MY SETTINGS CODE.  What the hell???  I look back at this and wonder what the hell brought me to the code that’s in place and that works.

I’m not going to expand on this too much but will in comments if we have discussion.  This is just one example of my insane confusion from trying to implement the Settings API for the second time and trying to actually follow directions.

Using update_option and get_option For Admin Settings in a Plugin

These methods are lightweight and serve a simple but awesome purpose.  You set a unique key and a value and those are accessible at any time.  I find it best just to learn by example so I will show what I recently did and explain along the way.

I need the ability to let an admin set a few checkbox options and a color.  For simplicity’s sake in this post I’ll show how I do that with just one checkbox and a text field (I use a color picker but that makes it complicated, so we will just use text).

Here’s the full code, explanation below that:

https://gist.github.com/ChrisFlannagan/26264438230bad687680c8cf61a8f5fa

Let’s look at the form first.  We have two inputs, feature_on and settings_color, a checkbox and a text input respectively.  These represent two settings we want the admin to be able to control.

Now let’s look at the php up top.  The first thing I do is set a default value $settings_color which will be used in case this has not been set by the admin yet.  Then I check if the form has been submitted by detecting if $_REQUEST[‘settings_color’] is set.  If it is, let’s update some settings.

The first method we use is the WordPress method update_option().  I love this method.  If the option doesn’t exist, it creates it, if it does exist then it simply updates it! So simple, so straight forward, so not messy, YES.

NOTE: Option keys MUST be unique so be carful naming as you don’t want to overwrite an option in another plugin.  Here I added “myplugin_” to the beginning but I’m sure you can be more creative than that.

Setting & Getting Text Input Values

This is so straight forward.  First thing we do after checking if the form was submitted is simply set the options value with the (sanitized) submitted value:

update_option( “myplugin_settings_color”, sanitize_hex_color( $_POST[‘settings_color’] ) );

Done! Now we can call this value and use it, overwriting our default set value with:

if( get_option( “myplugin_settings_color” ) !== false ) {
$settings_color = get_option( “myplugin_settings_color” );
}

We must check if the admin has already set this or not by testing if it exists via the !== false or we will throw a php warning.

Sanitizing with pre_update_option

It’s important to sanitize any posted data going into your database.  For options we use pre_update_option filter.  To use this you need to add your option’s key to the end of the filter.  So for my settings_color option it would be like this:

add_filter( 'pre_update_option_settings_color', array( &$this, 'hexcolor_sanitize' ), 10, 2 );

Then a function ‘hexcolor_sanitize’ needs to be defined for handling this.  In this code example below you will see how that is handled.  Side note, sanitize_hex_color is not available outside of the customizer so I just copied the function into my plugin’s code:

https://gist.github.com/ChrisFlannagan/ce8c3152552e7156ad2c892bcc7e85cb

Setting & Getting Checkboxes

If a checkbox is checked when a form is submitted then it’s value is accessible via $_REQUEST[‘checkbox_name’].  If it is NOT checked, then that request variable is not set at all.  So if we don’t need a value from it but simply need to detect if it’s checked or not we just see if it exists:

if( isset( $_REQUEST[feature_on] ) ) {

For my purposes this is all we need so we update our option to either true or false.  Now if we want to set the box to checked or unchecked in the admin interface based on what an admin has or has not set already then we need to check a couple of things.  Has this setting been set previously? If so is this setting true or false? We accomplish this with:

if ( get_option( “myplugin_feature_on” ) !== false && get_option( “myplugin_feature_on” ) ) {
echo ‘checked=”checked” ‘;
}

Conclusion

The purpose of this article isn’t to make the Settings API look like a bad choice.  It’s extremely powerful and a great choice for many applications.  But for simple plugins like mine it’s overkill.  And I will say this, the codex and documentation on the Settings API are fucking horrible and confusing.