Intro to the WordPress Settings API

API Documentation is always going to be a hell that I do my best to avoid. I learned early on when working with SourceSDK that documentation for much of anything worth a damn would be hard to find and the new WordPress Settings API (SAPI) is in the same ballpark. This was actually added to the 2.7 release of the trusty blogging / CMS software, but it was (was? is!) not well documented and has some really dope capabilities and features that you should all take advantage of.

So what are you waiting for? Time to rewrite a plug-in and learn the new API!

This walkthrough is going to do more than just talk about SAPI. I will use this soap box to share some suggestions to future plug-in developers to help ensure that you work is able to be self contained, avoiding function collisions and improve your ability to debug things. Proper programming standards (naming conventions, use of white space and the like) are all still quite applicable, even in light of your development of a plugin. The following are a few that I have picked up over the years.

  • Any plug-in should only have one variable added to the options. Use an array to be able to save more than that one variable.
  • All plug-in functions should be wrapped in a class.
  • If you need to create An Administration Menu item, don’t go overboard. Only insert what is entirely necessary and where it is appropriate.
  • Do not rely upon side effects.
  • Include proper Install, Upgrade/Update, Uninstall functionality

That said, know that there is a lot more to be uncovered. I will likely expand upon these with a later post, but i am a bit excited and I’d like to get into the meat of the discussion. For the purposes of this discussion i will be creating an array variable, something that is quite important to avoid bloating up the options table with settings that aren’t necessary in case of a bad update or uninstall.

Overview of the Settings API

The new API is actually pretty complicated, but only in its usage. The API only comes down to a handful of functions and concepts, but they are pretty powerful. SAPI wraps up a number of tasks to allow us to focus on dealing with the actual plug-in internals.

  • making sure an admin submitted our value
  • provides a hook to allow us to control validation of our plug-in values
  • allows us to embed our controls, the form elements you are used to finding on a website, in pages as we see fit.
    • This includes the built in pages within WordPress
    • but can also include making our own
  • Abstracts away the process of saving the verified values
  • Standardize formatting to WordPress default
  • White-listing variables, to simplify processing.
  • Error Catching & Display

The first function we are going to introduce is used to alert WordPress to our intention to work with the SAPI as well as let it know that we would like to keep an eye out for a particular variable.

register_setting(
    'Demo_Vars_Group',
    'Demo_Vars',
    array('DemoPlugin', 'Validate'));

This snippet creates a Demo_Vars_Group containing the Demo_Vars input and passes the callback to a function DemoPlugin::Validate(). This function call assumes a variable name of Demo_Vars which will be passed to the Validate function. As a quick side note, this function is necessarily static. If you don’t know what that means, please leave a comment and ill be sure to expand on what is going on here. There is a second function unregister_setting which is useful for situations where you are modifying another plug-in or wish to remove options from the default WordPress. I can’t really wrap my mind around where else you would use it so since this is a basic plug-in ill go ahead and ignore it. (We will also cover validation in a minute.)

The next key function is going to let SAPI know we want to create a section, which is used to wrap up our controls.

add_settings_section(
    'Demo_Vars_ID',
    'Demo Vars Title',
    array('DemoPlugin', 'Overview'),
    'Demo_Page_Title');

Demo_Vars_ID is the identifier for the section, used to bind the controls so WordPress knows which ones to render. Demo Vars Title goes into an H3 tag above the controls. Immediately below the title the contents of DemoPlugin::Overview() is dropped. You don’t have to return the contents, it is executed and should just render the content as though it were inline code. Lastly, ‘Demo_Page_Title’ is a unique identifier for the section, used when rendering the form.

Next we need to add a settings field to our section. This is one of the more complicated functions (lots of parameters, none of which are too complicated but can be difficult to figure out.).

add_settings_field(
    'Control_ID',
    'Demo Control Name',
    array('DemoPlugin', 'Demo_Control'),
    'Demo_Page_Title',
    'Demo_Vars_ID');

As you can see this has many similar fields to the previous function, but their order is different. ControlID is the expected ID or Name for the field that you are creating. As with other IDs within WordPress development, you should ensure that this is Unique.The third is another link to a function call, this one to DemoPlugin::Demo_Control(), which should also just return the content as though it were inline code. The last two are expected to be identical to the entries in the section. Using these two you can, however, do some hoop jumping and use the same section on two different pages with different controls inside of it, but i don’t recommend complicating life in such a way.

Now we need to do some ground work and setup our plug-in to execute these functions. All of these functions should be included when the admin sections are initialized. Our menu, on the other hand is rendered at a different time.

class DemoPlugin
{
    function Init()
    {
        register_setting(
            'Demo_Vars_Group',
            'Demo_Vars',
            array('DemoPlugin', 'Validate'));

        add_settings_section(
            'Demo_Vars_ID',
            'Demo Vars Title',
            array('DemoPlugin', 'Overview'),
            'Demo_Page_Title');

        add_settings_field(
            'Control_ID',
            'Demo Control Name',
            array('DemoPlugin', 'Demo_Control'),
            'Demo_Page_Title',
            'Demo_Vars_ID');
    }

    function Admin_Menus()
    {
        if (!function_exists('current_user_can')
            ||
            !current_user_can('manage_options'))
                return;

        if (function_exists('add_options_page'))
            add_options_page(
                'Settings_API_Sample',
                'Settings API Sample',
                'manage_options',
                'settings_api_sample',
                array('DemoPlugin', 'SampleForm'));
    }
}

add_action('admin_init',
    array('DemoPlugin', 'Init'));
add_action('admin_menu',
    array('DemoPlugin', 'Admin_Menus'));

With that you are using the API appropriately, setting up all of the necessary ground work. The only aspects left are to hook in your functions for the controls, overview, validation and the sample form. Each of them is pretty straight forward, so i will post them in line and expand where important.

function SampleForm()
{
                        $Demo_Vars = get_option('Demo_Vars');

                        ?>
                                

Settings API Sample

<input name="Submit" type="submit" class="button-primary" value="" />

< ?php }

We have introduced two new SAPI functions, do_settings_sections() and settings_fields() as well as a couple other standards. This is really the simplest format for a settings page. The SAPI will handle rendering all of the sections and controls for us, we only have to worry about dropping the submit button, directing our form over to the options.php page and making sure it is a post form. The do_settings_sections() function takes the ID of the section that we defined earlier, likewise for the ‘Demo_Vars_Group’ parameter sent to the settings_fields() call.

function Overview()
{
    ?>This is a demo of the Settings API.
<input id="SampleValue" class="regular-text" type="text" name="Demo_Vars[SampleValue]" value="" />
    < ?php
}

These two function calls are executed by the SAPI. You should note that the name attribute of the input is to take advantage of the internal processing of PHP to create an array of information. Using this standard makes the whole process easy.

And all that is left is the Validation function. The white-listing mentioned earlier registers the variable Demo_Vars and will pass the contents of it into the Validation function as a parameter. We should then validate it and return. A practice that I have adopted is to grab the existing values, validate the input and if things are kosher, update our entry in our existing array, then return the existing array. There are a number of benefits to this that i wont get into here considering how long this article is already.

function Validate($input)
{
    $Demo_Vars = get_option('Demo_Vars');

    // Validate & Replace with values from $input
    if (sprintf("%.0f", $input['SampleValue']) == $input['SampleValue'])
        $Demo_Vars['SampleValue'] = $input['SampleValue'];
    else
        add_settings_error('Demo_Vars',
            'settings_updated',
            __('Only integers are accepted.'));

    return $Demo_Vars;
}

And with that this plugin is complete. I added a validation check to confirm that the input value is an integer as well as a call to the add_settings_error() function, which registers an error/success message to the user. The ‘Demo_Vars’ entry

As with all other tutorials on my website, questions and comments are answered in the comments or email. Because of some formatting issues above I have attached the entire php file.

http://pastebin.com/YDBm3HHX

Enjoy!

21 thoughts on “Intro to the WordPress Settings API”

  1. Nice post, thanks for writing it!
    I’ve set up my plugin’s option page with the Settings API, and all is well, except for one thing.

    I would like to have the standard WordPress category selection checkbox list used on my plugin option page.

    If I am not using the SAPI, I can use ‘add_meta_box’ and ‘do_meta_box’ with the built in ‘post_categories_meta_box’ function. However… I really want to use the SAPI.

    I can get the category box display, but I do not see any way to hook in to the SAPI so I can give it the logic necessary to save what categories are selected.

    Any thoughts?
    Take care.

    1. You can play with arrays and such in a select or multi select with the function call, similar to what i did with my Demo_Control function. Within it you would just need to grab the entries you need and deal with it as you would in a regular PHP routine.

      Have you tried that already?

  2. Thank you for this post- the codex documentation is a little thin. One thing I am stuck on is the code for the callback function that generates the input. Across all the tutorials this code is static, but that seems really limiting. If you have 10 textbox inputs, you wouldn’t want to have 10 textbox callbacks- when you could just have 1 callback that accepts arguments. I haven’t gotten a dynamic version to work yet, but this has to be possible right?

    1. You can also use global variables if you are daring, or do some SQL Queries and get the results in the function. There are many ways to pull it off.

  3. Thanks for the nice post. I finally made the jump and updated my admin screen with your help. I liked your use of the add_settings_error() function which other posts in the Internet didn’t include. There’s not much documentation on that function and it would be nice if an error CSS class would be added to the table cell of the offending element so that I can highlight the error field in red. Any idea how to do it?

  4. I think the most straight forward route would be to simply add a flag and when rendering the control, as i do in Demo_Control, you can check that flag and easily change the class, append text, change div style attributes and so forth without much of a modification.

    There is also a couple functions available that could be useful for testing/rendering of values:
    get_settings_errors – Handy if you have many settings errors being checked
    settings_errors – Handy if you want to just see if there was an error

    The value passed back from the first call is an array of arrays like the following:
    array(
    ‘setting’ => $setting,
    ‘code’ => $code,
    ‘message’ => $message,
    ‘type’ => $type
    );

    which you can filter based on setting just as easily as you can elsewhere in your plugin.

  5. Be wary of the pingback, it is not stylistically sound, and the programming logic is in need of sprucing up. It does illustrate how to catch a single error. By and large, however, the SAPI will show all errors on its own without this interaction. The approach he has provided is useful for changing the colors of fields and the like, but be aware that you will need to do some thinking as his code is only an example.

  6. Hey,
    i was wondering how to pass variables to callback functions called in functions like add_settings_field.
    I tried the following two approaches and none of them worked!
    add_settings_field(
    ‘Control_ID’,
    ‘Demo Control Name’,
    array(‘DemoPlugin’, array(‘Demo_Control’, ‘Variable’)),
    ‘Demo_Page_Title’,
    ‘Demo_Vars_ID’);
    add_settings_field(
    ‘Control_ID’,
    ‘Demo Control Name’,
    array(‘DemoPlugin’, ‘Demo_Control’, ‘Variable’),
    ‘Demo_Page_Title’,
    ‘Demo_Vars_ID’);

    Do you know any way to solve this?

  7. You don’t typically send variables in with that function, but you can with the args parameter. That entry is a callback type and passed in its entirety through to call_user_func.

    If you look into the functions signature you will see an extra parameter $args = array(); If you add that parameter you will be able to pass in a set of arguments to your callback:

    function add_settings_field($id, $title, $callback, $page, $section = ‘default’, $args = array()) {

    $wp_settings_fields[$page][$section][$id] = array(‘id’ => $id, ‘title’ => $title, ‘callback’ => $callback, ‘args’ => $args);

    call_user_func($field[‘callback’], $field[‘args’]);

  8. Unluckily I don’t understand much of this. I have a small problem that I can’t find a solution yet on the net.
    All articles I read about Settings pages on WP, are using get_option, but what if my data is in another database and not in wp_options? Than I can’t use get_options. I tried using the global wpdb and making a query, but than my settings page crashes.

    1. That is a kind of odd situation in and of itself. WordPress uses its own database, and if its in something else you should be turning to the database functions of PHP to grab it, but then it becomes less about wordpress settings. I would suggest either you merge your settings into the wordpress settings world, or you dont use wordpress for your plugin, or you use the database functionality.

      If you mean its in a different table, that is typically alright, but again, it wont be fun to grab. Again, i would turn to the php db interaction to grab it.

      Can you be more specific about your errors when it crashes? What actually happens?

  9. Hi there – I have just started a new job and trying to get up to speed with WordPress – primarily spent past 2 1/2 years using Umbraco… This tutorial is ideal to aid my understanding of how to add functionality to a site. However i have not been able to get it to work and it would appear the code is no longer available for download. I havae activated my plugin and the option is available under “Settings” however no form appears – I must be missing something obvious. Are you able to provide the source code for me to compare against? Cheers, Nigel

  10. Hi, hope you read this.

    I gat trouble with the add_settings_error() function, i have a lot of inputs to sanitize , but i can’t get it work

    some of mi validation code are like this
    $input[‘iflowid’] = ( $input[‘iflowid’] == ” ? ‘iflows’ : $input[‘iflowid’] );
    $input[‘iflowid’] = str_replace(” “, “_”, $input[‘iflowid’]);
    $input[‘aspect_ratio’] = ( !is_numeric($input[‘aspect_ratio’]) ? 1.964 : $input[‘aspect_ratio’] );
    …..

    Can you explain what happens

    Tks in advance

    1. I have no idea what you are trying to do there, but since this is not an interview i wont waste either of our time telling you how PHP works. If you have a more direct and or interesting question i would be happy to help, otherwise i would suggest #Help on the phpfreaks irc server for further information on debugging your code.

Leave a Reply