Francis Yaconiello

How To Handle WordPress Settings

February 16, 2013

Wordpress Settings

In response to a previous article which covers custom WordPress plugin structure, How to Write a WordPress Plugin, a reader asked me the following question.

What are the advantages of using this approach then using add_settings_field() and add_settings_section()?

My previous article covers one method of adding a settings page to a plugin. In this article, I'm going to elaborate on WordPress's settings framework.

Create a settings.php File and Define Your Settings Class

Class based plugins are easier to understand and maintain. In my previous tutorial I separated the custom post types into their own classes and files. In this example, I'm going to do the same for the plugin's settings. Create a settings.php file in your main plugin directory and add the following.

<?php
if(!class_exists('WP_Plugin_Template_Settings'))
{
    class WP_Plugin_Template_Settings
    {

        // Settings related functions go here

    } // END class WP_Plugin_Template_Settings
} // END if(!class_exists('WP_Plugin_Template_Settings'))

In your main plugin's php file, add the following lines to the constructor.

<?php
// Initialize Settings
require_once(sprintf("%s/settings.php", dirname(__FILE__)));
$WP_Plugin_Template_Settings = new WP_Plugin_Template_Settings();

Register Your settings

Before all else, every setting that you register needs to be registered with WordPress. This is extremely straight forward, for each setting you wish to manage using the settings framework you must call register_setting (http://codex.wordpress.org/Function_Reference/register_setting).

<?php
register_setting($option_group, $option_name, $sanitize_callback);

$option_group (string | required) is an identifier for grouping options. To keep things organized and simple I generally use the theme/plugin slug that the settings are used to manage. In the case of a plugin named "WP Plugin Template" I used an $option_group of "wp_plugin_template-group". The "-group" part of the identifier comes in handy when your plugin/theme/etc is more complicated and requires different groups of settings. As a general rule of thumb, if I want the settings fields to be separated into seperate form fieldsets I use seperate "-group" ids. ie: "wp_plugin_template-group_a" and "wp_plugin_template-group_b"

$option_name (string | required) is the name of the option I want to save. I don't get too fancy with these, use general variable naming convention. The option name should indicate what kind of value is stored in it.

$sanitize_callback (string | optional) a callback function for validation and sanitation. You can put any validation you want in this callback function, BUT the function must return a value of some kind. So if for the supplied value doesn't pass your validation rules, return an empty string return '';.

Where in my code should I register my settings?

See the previous article, How to Write a WordPress Plugin, for where I generally register settings. As a general rule: hook into the admin_init action and do it there. see http://codex.wordpress.org/Plugin_API/Action_Reference/admin_init

<?php
/* In your settings class */

public function __construct()
{
    // register actions
    ...
    add_action('admin_init', array(&$this, 'admin_init'));
    ...
}

public function admin_init()
{
    ...
    // register your plugins settings
    register_setting('wp_plugin_template-group', 'setting_a');
    register_setting('wp_plugin_template-group', 'setting_b');
    ...
}

Where do you want the user to manage these settings?

Your first real choice is the page that you want your admins to manage these settings. You can use an existing page such as "media" or "general" OR create a new page. In my plugin tutorial, I choose the latter and create a new settings type page.

Creating a new WordPress admin page

Adding a new page is fairly easy, just hook into the admin_menu action and call a wrapper function for the type of submenu page you need. I generally like to use add_options_page. see http://codex.wordpress.org/Administration_Menus#Using_Wrapper_Functions) for a list of wrapper functions

<?php
/* In your settings class */

public function __construct()
{
    // register actions
    ...
    add_action('admin_menu', array(&$this, 'add_menu'));
    ...
}

/**
 * add a menu
 */     
public function add_menu()
{
    // Add a page to manage this plugin's settings
    add_options_page(
        'WP Plugin Template Settings', // Page Title
        'WP Plugin Template', // Menu Title
        'manage_options', // required permission/capability
        'wp_plugin_template', // menu slug
        array(&$this, 'plugin_settings_page') // callback
    );
} // END public function add_menu()

At this point if you are using a custom page to display your settings you could conceivably just echo html from your plugin_settings_page function and everything would be awesome. Except, you would have mixed template/HTML logic in with your application/plugin logic. Instead, lets include a template from our templates directory w/i the callback. That way the template logic is separate.

<?php
/* In your settings class */

/**
 * Menu Callback
 */     
public function plugin_settings_page()
{
    if(!current_user_can('manage_options'))
    {
        wp_die(__('You do not have sufficient permissions to access this page.'));
    }

    // Render the settings template
    include(sprintf("%s/templates/settings.php", dirname(__FILE__)));
} // END public function plugin_settings_page()

Now you need a templates/settings.php file in order to render the content from plugin_settings_page. If you are NOT using add_settings_section and add_settings_field then you can simply add your desired HTML in this file like so.

<div class="wrap">
    <h2>WP Plugin Template</h2>
    <form method="post" action="options.php"> 
        <?php @settings_fields('wp_plugin_template-group'); ?>
        <?php @do_settings_fields('wp_plugin_template-group'); ?>

        <table class="form-table">  
            <tr valign="top">
                <th scope="row"><label for="setting_a">Setting A</label></th>
                <td><input type="text" name="setting_a" id="setting_a" value="<?php echo get_option('setting_a'); ?>" /></td>
            </tr>
            <tr valign="top">
                <th scope="row"><label for="setting_b">Setting B</label></th>
                <td><input type="text" name="setting_b" id="setting_b" value="<?php echo get_option('setting_b'); ?>" /></td>
            </tr>
        </table>

        <?php @submit_button(); ?>
    </form>
</div>

With this you have a working settings page from the menu sup page you created. But what if you wanted to not write any form field HTML? What if you wanted to add your settings to an existing menu page? The next couple sections go over using add_settings_section and add_settings_field and the benefits/disadvantages of doing so.

Add a Setting Section

A setting section is simply a logical grouping of settings. Earlier I wrote about grouping settings and using unique $group_names based on form fieldsets. Each Section needs a unique name as well. see http://codex.wordpress.org/Function_Reference/add_settings_section

<?php
/* In your settings class */

public function __construct()
{
    // register actions
    ...
    add_action('admin_init', array(&$this, 'admin_init'));
    ...
}

public function admin_init()
{
    ...
    // register your settings section
    add_settings_section(
        'wp_plugin_template-section', 
        'WP Plugin Template Settings', 
        array(&$this, 'settings_section_wp_plugin_template'), // callback for this section
       'wp_plugin_template' // or 'media' or 'wp_plugin_template' if you created a page with that $menu_slug in the previous step
    );
    ...
}

/**
 * This callback provides a help-text like blurb above a settings section
 */
public function settings_section_wp_plugin_template()
{
    // Think of this as help text for the section.
    echo 'These settings do things for the WP Plugin Template.';
} // END public function settings_section_wp_plugin_template()

Add Settings Fields

Now you have registered settings and chosen/created a page to display your settings and created a settings section. The last step is to add fields to that section for each of your settings. see http://codex.wordpress.org/Function_Reference/add_settings_field

<?php
/* In your settings class */

/**
 * hook into WP's admin_init action hook
 */
public function admin_init()
{
    ...
    // add your setting's fields
    add_settings_field(
        'wp_plugin_template-setting_a', // the unique name for this settings field
        'Setting A', // title/label
        array(&$this, 'settings_field_input_text'), // Callback to render this field's input element
        'wp_plugin_template', // page
        'wp_plugin_template-section', // section
        array(
            'field' => 'setting_a'
        ) // optional key/value pairs that get passed to the callback
    );
    add_settings_field(
        'wp_plugin_template-setting_b', 
        'Setting B', 
        array(&$this, 'settings_field_input_text'), 
        'wp_plugin_template', 
        'wp_plugin_template-section',
        array(
            'field' => 'setting_b'
        )
    );
    ...
} // END public static function activate

Note: I'm using the $args array to make this callback reusable for all of my input type="text" fields. You could do a separate callback for each settings field, this is a tiny bit neater.

<?php
/* In your settings class */

/**
 * This function provides text inputs for settings fields
 */
public function settings_field_input_text($args)
{
    // Get the field name from the $args array
    $field = $args['field'];
    // Get the value of this setting
    $value = get_option($field);
    // echo a proper input type="text"
    echo sprintf('<input type="text" name="%s" id="%s" value="%s" />', $field, $field, $value);
} // END public function settings_field_input_text($args)

What About templates/settings.php?

Now that you are using settings fields and sections you can refactor your settings page template to not have field related markup. see http://codex.wordpress.org/do_settings_sections

<div class="wrap">
    <h2>WP Plugin Template</h2>
    <form method="post" action="options.php"> 
        <?php @settings_fields('wp_plugin_template-group'); ?>
        <?php @do_settings_fields('wp_plugin_template-group'); ?>

        <?php do_settings_sections('wp_plugin_template'); ?>

        <?php @submit_button(); ?>
    </form>
</div>

What If I Decided Not To Use a Custom Page?

If you decided to not create a sub menu page for your settings and use and existing page such as general or media, then you do not need a templates/settings.php template. Instead the page will automatically render your settings section at the bottom of the page after all of the existing sections.

Full settings.php Listing

<?php
if(!class_exists('WP_Plugin_Template_Settings'))
{
    class WP_Plugin_Template_Settings
    {
        /**
         * Construct the plugin object
         */
        public function __construct()
        {
            // register actions
            add_action('admin_init', array(&$this, 'admin_init'));
            add_action('admin_menu', array(&$this, 'add_menu'));
        } // END public function __construct

        /**
         * hook into WP's admin_init action hook
         */
        public function admin_init()
        {
            // register your plugin's settings
            register_setting('wp_plugin_template-group', 'setting_a');
            register_setting('wp_plugin_template-group', 'setting_b');

            // add your settings section
            add_settings_section(
                'wp_plugin_template-section', 
                'WP Plugin Template Settings', 
                array(&$this, 'settings_section_wp_plugin_template'), 
                'wp_plugin_template'
            );

            // add your setting's fields
            add_settings_field(
                'wp_plugin_template-setting_a', 
                'Setting A', 
                array(&$this, 'settings_field_input_text'), 
                'wp_plugin_template', 
                'wp_plugin_template-section',
                array(
                    'field' => 'setting_a'
                )
            );
            add_settings_field(
                'wp_plugin_template-setting_b', 
                'Setting B', 
                array(&$this, 'settings_field_input_text'), 
                'wp_plugin_template', 
                'wp_plugin_template-section',
                array(
                    'field' => 'setting_b'
                )
            );
            // Possibly do additional admin_init tasks
        } // END public static function activate

        public function settings_section_wp_plugin_template()
        {
            // Think of this as help text for the section.
            echo 'These settings do things for the WP Plugin Template.';
        }

        /**
         * This function provides text inputs for settings fields
         */
        public function settings_field_input_text($args)
        {
            // Get the field name from the $args array
            $field = $args['field'];
            // Get the value of this setting
            $value = get_option($field);
            // echo a proper input type="text"
            echo sprintf('<input type="text" name="%s" id="%s" value="%s" />', $field, $field, $value);
        } // END public function settings_field_input_text($args)

        /**
         * add a menu
         */     
        public function add_menu()
        {
            // Add a page to manage this plugin's settings
            add_options_page(
                'WP Plugin Template Settings', 
                'WP Plugin Template', 
                'manage_options', 
                'wp_plugin_template', 
                array(&$this, 'plugin_settings_page')
            );
        } // END public function add_menu()

        /**
         * Menu Callback
         */     
        public function plugin_settings_page()
        {
            if(!current_user_can('manage_options'))
            {
                wp_die(__('You do not have sufficient permissions to access this page.'));
            }

            // Render the settings template
            include(sprintf("%s/templates/settings.php", dirname(__FILE__)));
        } // END public function plugin_settings_page()
    } // END class WP_Plugin_Template_Settings
} // END if(!class_exists('WP_Plugin_Template_Settings'))

Code Tutorials WordPress


comments powered by Disqus