Consolidate Options with Arrays in your WordPress Plugins

If you’ve ever written a plugin for WordPress you’ve probably dealt with giving the end user options. Unless you’ve taken the low road and forced the user to directly edit the plugin file, “options” means a Settings screen in the WordPress admin, and most likely you are storing those options in the blog’s wp-options table. Over time, bloggers using various plugins can end up with hundreds of extraneous records in their options table*. If they ever stop using a plugin, or if they ever want to manually clear out options, this job can be made difficult by all the clutter, and the problem is made far worse when plugin authors add a new record for each separate setting in a plugin. Beyond the mess, plugins written this way generally make many more calls to the database than they need to, which adds unnecessary burden on the server.

I have seen and used plugins that added anywhere from ten to thirty new records to the options table, and a correspondent of mine reports that he has encountered a plugin that, upon activation, adds a whopping 140 new records to wp_options. Egad! There has to be a better way!

There is a better way. With a few simple changes to your code, plugin authors can put all the various settings into an array, and enter that array as a single record in the options table. This article will show you how.

On this site I use a plugin called ShrinkyLink. It performs the straightforward but useful function of shortening long URLs to a specified length, or replacing the link text entirely with a specified string. It was created by Andrew Rader, but as far as I can tell his web site no longer exists. For the purposes of this article, it has a number of good qualities:

  • It inserts 10 separate records into the WordPress options table
  • It is not likely to see further updates from the original author
  • It is useful and thus worthy of updating
  • The original author was kind enough to release it under the GPL2 license

The last version of ShrinkyLink was 0.2, released in 2006. Today we’re going to begin the rehabilitation of this plugin and create a version 0.3 that stores all its settings in a single array.

I’m going to split this into multiple sections:

  1. How to set and retrieve settings using arrays
  2. Upgrading your plugin’s Admin Screen form to use an array
  3. Upgrading existing settings from multiple records to a single array

Let’s get to it.

How to set and retrieve settings using arrays

WordPress allows plugin authors to easily add, manipulate, and retrieve records in the wp_options table, using the built-in add_option(), update_option(), get_option(), and delete_option() functions. When we use arrays for our settings, we will continue to use all of those functions, but we’re going to change the way we pass information through them.

If you would like to follow along with source files, download ShrinkyLink 0.2 and open up shrinkylink.php.

Let’s take a look at the original function that sets up the default settings. This is set to run when the plugin is activated:

function shrinky_init() {
	if( function_exists('add_option') ) {
		add_option( 'shrinky_comments', 'yes' );
		add_option( 'shrinky_posts', 'no' );
		add_option( 'shrinky_replace', 'yes' );
		add_option( 'shrinky_text', 'link' );
		add_option( 'shrinky_trim', 'no' );
		add_option( 'shrinky_size', '12' );
		add_option( 'shrinky_scheme', 'no' );
		add_option( 'shrinky_www', 'no' );
		add_option( 'shrinky_elipse', 'yes');
		add_option( 'shrinky_domain', 'yes' );
	}
}

Right out of the gate, that will add ten separate records to the blog’s options table.

We’re going to change it to something that looks similar, but does something quite different. First off, let’s decide what to call our new single setting. We might just go with “shrinkylink”, or “shrinkylink_settings”, but part of the point of this exercise is to keep the options table tidy; and in the interests of clarity, I’d like to go the further step of specifying in the option name that it was made by a plugin. Let’s use “plugin_shrinkylink_settings”.

Here’s the new function:

function shrinky_init() {
	$new_options = array(
		'comments' => 'yes',
		'posts' => 'no',
		'replace' => 'yes',
		'trim' => 'no',
		'text' => 'link',
		'size' => '12',
		'scheme' => 'no',
		'www' => 'no',
		'elipse' => 'yes',
		'domain' => 'yes'
	);

	add_option( 'plugin_shrinkylink_settings', $new_options );
}

You’ll note that I eliminated the if( function_exists('add_option') ) test. it was a diligent, but I think unnecessary, step. There’s no harm in leaving it in if you prefer.

Otherwise this is very straightforward. We set the various options as elements of a new array, and then use add_option() (or update_option()) to pass the entire array to the database. You should also note that I stripped the “shrinky_” off of the names of the individual settings. That was there to identify that those settings came from this plugin; but it’s no longer needed as they are contained within an array that identifies them already.

How does retrieving settings change? Previously, any time the code needed a setting, it would make a call to the database. If a function needed five different settings, that was five calls to the database:

get_option('shrinky_comments');
get_option('shrinky_posts');
get_option('shrinky_size');
get_option('shrinky_scheme');
get_option('shrinky_www');

With an array, we can make a single call to the database, and set the result to a variable. Then as we need the various settings, we don’t call the database again, we simply look at the variable:

$options = get_option('plugin_shrinkylink_settings');

$options['comments'];
$options['posts'];
$options['size'];
$options['scheme'];
$options['www'];

The same holds true for setting options. Rather than multiple calls to the database:

update_option('shrinky_comments', 'new value');
update_option('shrinky_posts', 'new value');
update_option('shrinky_size', 'new value');
update_option('shrinky_scheme', 'new value');
update_option('shrinky_www', 'new value');

…we can update the variable, and then set the option once at the end of our function:

$options = get_option('plugin_shrinkylink_settings');

$options['comments'] = 'new value';
$options['posts'] = 'new value';
$options['size'] = 'new value';
$options['scheme'] = 'new value';
$options['www'] = 'new value';

update_option('plugin_shrinkylink_settings', $options);

It is important to note that when we call update_option() at the end of the function, it must contain all of the settings, not just the ones we updated. Do not just create a new $options variable and set a few settings — instead, populate $options using get_option(), then makes changes, then update.

In the ShrinkyLink plugin, the author made an interesting choice: he completely abstracted setting and reading of options into individual functions. There is some advantage to this type of thing, but he took it much further than I would have: For each option there is a separate function for reading it or setting it, and for booleans (simple on/off) he actually has two functions — one for turning it on, and one for turning it off! This means that each of the original ten settings has two or three functions each — a grand total of twenty-eight separate functions just for reading or settings those ten options.

In this case it actually makes updating the plugin for the array quite easy, if a bit tedious.

Original “set yes” function:

function enable_comment_filter() {
	if( function_exists('update_option') ) {
		update_option( 'shrinky_comments', 'yes' );
	}
}

And the new version:

function enable_comment_filter() {
	$options = get_option('plugin_shrinkylink_settings');
	$options['comments'] = 'yes';
	update_option( 'plugin_shrinkylink_settings', $options );
}

At this level of abstraction the arrays lose their efficiency, as we end up making a call to read and a call to set every time we change an option. I personally plan to adopt this orphaned plugin and improve the code quite a bit, including eliminating the excess of get/set functions in favor of interacting with the array directly in the larger functions. This problem is specific to the setup of this particular plugin, and dealing with that goes beyond the scope of this article.

I’m not going to go over the changes to all of these little functions, as they are redundant, and by this point should be obvious. I leave the remainder as an exercise to you, the reader, should you choose to follow through….

Upgrading your plugin’s Admin Screen form to use an array

Okay, now that we know how to deal with the array directly, we need to adjust our Admin screen to handle our new settings properly. If you’re following along in source, open up shrinky-ui.php.

Within the form itself, we need to modify the form fields to put the values into an array, so that that array is past to $_POST when we submit the form. This is easier than it sounds.

Normally, each form field has a “name” attribute, and the value is passed to $_POST with that name attached to it. All we need to do is alter those names so they are formatted as an array instead of a bunch of separate strings. So rather than <input name="comments" ...>, we do something like this: <input name="shrinkylink[comments]" ...>. Every field should be given a name attribute in the form of arrayname[field]. The array name can be anything, but the field name should be the exact same name as that of the corresponding setting in the array we’ll be putting in the options table.

Heading back to the top of the form, you can see a whole bunch of code within an if clause: if( isset($_POST['info_update']) ) { ... } Delete that whole big chunk of code, we’re going to replace it with this:

if( isset($_POST['info_update']) ) {
	$new_options = $_POST['shrinkylink'];
	$bool_opts = array( 'comments', 'domain', 'elipse', 'posts', 'scheme', 'www' );
	foreach($bool_opts as $key) {
		$new_options[$key] = $new_options[$key] ? 'yes' : 'no';
	}
	update_option( 'plugin_shrinkylink_settings', $new_options);
	echo '<div id="message" class="updated fade"><p><strong>' . __('Settings saved.') . '</strong></p></div>';
}

So first we’re setting the variable $new_options to the “shrinkylink” array that the form passed to $_POST.

Next, since the boolean settings for this plugin are set up to accept explicit “yes” or “no”, I’m filling a new array, $bool_opts, with the names of all of the yes/no options, and then setting the “yes”es and “no”es with a quick foreach loop. This is particular to this plugin, and may differ in yours. You may set it to true/false, “on”/”off”, or leave it as is. Personally I prefer true/false, but I will leave it for now.

Finally, we pass the array to the options table, and echo “Settings saved” to the screen.

Upgrading existing settings from multiple records to a single array

The changes we’ve made to this plugin are great if somebody is going to use it for the first time; but what about somebody who used the previous version and has now upgraded to our new version? We still need to set the new options array according to the old settings, and then clean up the old settings. Fortunately, upgrading from separate options records to an array is surprisingly easy. Let’s take a look at the new, improved init function:

function shrinky_init() {
	$new_options = array(
		'comments' => 'yes',
		'posts' => 'no',
		'replace' => 'yes',
		'trim' => 'no',
		'text' => 'link',
		'size' => '12',
		'scheme' => 'no',
		'www' => 'no',
		'elipse' => 'yes',
		'domain' => 'yes'
	);

	// if old options exist, update to new system
	foreach( $new_options as $key => $value ) {
		if( $existing = get_option( 'shrinky_' . $key ) ) {
			$new_options[$key] = $existing;
			delete_option( 'shrinky_' . $key );
		}
	}

	add_option( 'plugin_shrinkylink_settings', $new_options );
}

First we set up the array of defaults as we did before. Before we run the add_option() our new code will run. What it does is:

  • Loop through the array of default options
  • If the wp_options table contains a record the name of which is “shrinky_” + the name of a new option:
    1. set the new option to the value of the old one, and
    2. delete the old option from the table

Then we assign the new array to the table as we did before, and we’re done!

…almost.

You will note that in the Admin screen there is a set of two radio button with a name of “shrinky[mode]“, and values of “replace” and “trim” — but in the original plugin, “replace” and “trim” are two separate settings. Here’s your final exam: What changes need to be made to smoothly incorporate those two settings into the single “mode” setting? Hint: Nothing further needs to be done to the Admin Screen code. Be sure you also transfer the old setting to the new format.

To see the final result, you can download my final ShrinkyLink 0.3. Again, I plan to further clean up and develop this plugin; this version simply reflects the methods discussed in this article.

That’s all for now. I hope you all found this useful.

Coming next: Contain your plugins’ code in classes to avoid collisions and increase reusability

Previously: Give your WordPress plugin credit without cluttering the GUI

[Update July 12: Added mention of extraneous database calls in first paragraph.]

*:Some of you have probably noticed an obvious point that I have overlooked here: Giving users the direct ability to clean up anything that your plugin does to the database. Uninstall is a topic unto itself, and I hope to discuss that in a future article.
This entry was posted in Codecraft, Webcraft and tagged , , , , , , , , . Bookmark the permalink.

38 Responses to Consolidate Options with Arrays in your WordPress Plugins

  1. NG your idea is alright in a way but if you think about it an options table would be empty if not for plugins.

    So the solution I think is that core wordpress installation options should be in their own table, a far more important table.

    Also a new add_optionsgroup function would be good where we can do something like…

    $names = “example1,example2,example3″;
    $values = “value1,value2,value3″;
    add_optiongroup(‘name1′,’name2′)

    Simple I know I dont have to spell it out like that really but my point is this simple solution would encourage what this page is all about.

    WebTechGlobal.co.uk

  2. Francesco says:

    I’d find useful a function to retrieve a single element of an array with something like:

    get_option(‘options_array’,’key1′)

    To avoid:

    $tmp = get_option(‘options_array’);
    $value1 = $tmp['key1'];

    It would make the life easier in tempalte tags when you check some value in if statements:

  3. Francesco says:

    ?php if(get_option(?options_array?,’key1?)==’yes’): ?

  4. Hey! Great stuff. Thanks, I needed this. Since I’m kinda a plugin noob maybe you can help me out a bit?

    1) My plugin has quite a few setting that loading up an array and storing that array in options is ideal for. But how do I not over write that array? I know that’s really stupid and obvious but… Do I just test for the option existing and not reload the array (unless say, there’s a reset option)?

    2) What about post_meta, can that handle an array? How might you suggest managing 30+ “parms” at the per post level?

    Hopefully you’re still keeping an eye on this thread. Thx.

    • Stephen R says:

      Mark –

      1) You’re not going to overwrite anything unless you write the option back to the database. To save changes, call the entire array, change whatever elements you want, and then you write the entire array back to the database.

      2) I’m pretty sure you can write an array to post_meta, though I’ve never had to do such myself.

      Note that if the array is *really* large, it may be good to break it up into a couple different arrays (saved to different options or meta). Depends on the usage, but you may find that certain settings are only called in admin, for example.

  5. Milan says:

    Stephen, you made mistake in comment 12, value for unset should be in brackets.

    Also, register_setting and add_settings_field make it even simpler to use options (with array too).

  6. The Frosty says:

    Just curious on how to change an old array of names to new names?
    I’ve done this for my plugin, but a new version updates the names in the option. Not sure on how to best migrate to the new names.
    Example:

    New:
    $settings = array(
    ‘version’ => ’0.8′,
    ‘custom’ => false,
    ‘custom_css’ => ”,
    ‘custom_html’ => ”,
    );
    Old:
    $old = array(
    ‘use_custom’ => false,
    ‘cl_login_custom_code’ => ”,
    ‘cl_login_custom_html_code’ => ”,
    );

    Not sure the best way to go about this..

  7. Nabeel says:

    @The frosty, follow this tutorial to change array key names: http://nabtron.com/how-to-change-array-key-name-in-php/...

    and thanks for the tutorial! it’s bit lengthy btw! needed it though :) tired of creating plugins with tons of update and get_option!

  8. Jürgen says:

    Thank you,
    I just needed that for my new plugin. It used to get overcrowded with options.
    Now I put them inside an array (could have thought of that myself earlier) and done.
    Jürgen

  9. Nail Yener says:

    Hi Stephen,

    I have been checking WordPress plugin development articles and tutorials for the last three days and I can say that yours are among the best ones. Especially this array tip was extremely helpful for my plugin which will have many options.

  10. Pavel says:

    Thank you, Stephen!
    Article was quite useful and is very well written =)
    Thank u again and very best luck!

  11. Dan Brown says:

    Really good article – you write so well I understand it without any effort. Write more :)

  12. Coestecnic says:

    Great tutorial, but does worpress serialize array automaticly?

  13. Mks says:

    Hi
    i cant place single quote around form field like shrinkylink['comment'] cause my whole form i am displaying like echo ‘all form codes’; furthermore if i double quote around it then don’t works.help me out

  14. Vinoth Kumar says:

    Hi, Do you have any idea about get_post_meta(), add_post_meta() and update_post_meta() to add and get the value in array() ?

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>