A Full WordPress Plugin Example — Part 5 Settings page

In this part, we will add a settings page for our plugin GCMovie . In the settings page, there will be a field for the user to set whether to delete all the movie posts on uninstall. It looks like this:

GCMovie settings page

As what is mentioned on WordPress plugin handbook- Settings , WordPress provides two set of APIs that can help us to add a settings page semi-automatically. They do make us do less work and bring some other benefits like consistent display and robustness, but we need to take some time to figure out how to use these APIs.

Image how we build a settings page without such help. First we register a new page to the parent menu, in the page we read old option values if existing from the database and display them in a form. Then we need to handle the form submission to write the new values back to the database. A process can be automated somehow, isn’t it? After such a thinking , it helps to understand the ideas behind these two set APIs. But probably you make it clear till you finish all the whole example.

Parts of the rough process to build a settings page with Setting API and Options API would be:

  • Add a new page to the GCMovie’s menu with add_submenu_page().
    // Add a new page "gcmovie_settings"
    add_submenu_page(
      $parent,
      esc_html__( 'Settings', 'gcmovie' ),
      esc_html__( 'Settings', 'gcmovie' ),
      'manage_options',
      'gcmovie_settings',         // menu_slug
      array( $this, 'show_page' ) // callback to display the page
    );
    
  • Register a new setting with register_setting(), it will also create an entry in the wp_options table.
    // Register a new setting 'gcmovie_options' for "gcmovie_settings" page
    register_setting( 
      'gcmovie_settings', // option group, correspond to an allowed option key name
      'gcmovie_options',  // option name
      array( $this, 'sanitize' ) // callback to sanitize the option's value
    );
    

    You may specify more arguments like the type, description or default value for the option.

    Here it will create an option with name “gcmovie_options”, the value of the option can be a single value or an array object ( in database it is converted to a json) which can hold multiple key-value pairs.

  • Add new sections to the existing page with add_settings_section().
    // Add a new section "gcmovie_settings_section" to "gcmovie_sttings" page
    add_settings_section(
      'gcmovie_settings_section',          // id, identify the section.
      __( 'After uninstall', 'gcmovie' ),  // title
      array( $this, 'display_section' ),   // callback to display html for the seciton
      'gcmovie_settings'                   // page
    );
    
  • Add new fields to existing sections with add_settings_field().
    // Add a new filed to section "gcmovie_settings_section" 
    add_settings_field(
      'gcmovie_settings_field',             // id to identify the field.
                     // As of WP 4.6 this value is used only internally
                     // Use $args's "label_for" to populate the id inside the callback
      __( 'Delete movies', 'gcmovie' ),     // title
      array( $this, 'display_field' ),      // callback to display html for an option
      'gcmovie_settings',                   // page
      'gcmovie_settings_section',           // section
      [                                     // $args
          'label_for' => 'delete_movies_after_uninstall', 
                                // It is used to populate the "for" attribute of <label> 
                                // that wraps the title
      ]
    );
    

Now let’s start our work.

Create a setting class

Create a file named class-gcmovie-setting.php under inc folder and add below code:

<?php
/*
 * GCMovie setting page
 */

if ( ! defined( 'WPINC' ) ) {
    die( 'No direct access.' );
}

class GCMovie_Setting {
}

Register a setting page to menu

Add below function to GCMovie_Setting to add a setting page to our GCMovie menu.

    function register_pages() {
        $parent = 'edit.php?post_type=gcmovie';

        // Settings page
        add_submenu_page(
            $parent,
            esc_html__( 'Settings', 'gcmovie' ),
            esc_html__( 'Settings', 'gcmovie' ),
            'manage_options',
            'gcmovie_settings',
            array( $this, 'show_page' )
        );
    }

Register “admin_init” action

Add a constructor function and inside it register an callback to admin_init action. For register_setting() and add_settings_*() functions should all be added to the admin_init action hook.

    function __construct() {
        add_action( 'admin_init', array( $this, 'register_setting' ) );
    }

Register a new setting

In the callback register_setting(), we register a new setting, add a new section to the page and add a field to the section.

   function register_setting() {
        // Register a new setting with option_group, option_name and a santize callback
        // which is used to sanitize the option's value before saving it to database.
        register_setting( 'gcmovie_settings', 'gcmovie_options', array( $this, 'sanitize' ) );

        add_settings_section(
           'gcmovie_settings_section',
           __( 'After uninstall', 'gcmovie' ),
           array( $this, 'display_section' ),
           'gcmovie_settings'
        );

        add_settings_field(
            'gcmovie_settings_field',  // As of WP 4.6 this value is used only internally
                                       // Use $args' label_for to populate the id inside the callback
            __( 'Delete movies', 'gcmovie' ),
            array( $this, 'display_field' ),
            'gcmovie_settings',
            'gcmovie_settings_section',
            [
               'label_for' => 'delete_movies_after_uninstall', // id of the option 
            ]
        );
    }

Sanitize the option’s value before saving it to database

Add our sanitize function to sanitize the option’s value.

    function sanitize( $input ) {
        $new_input = array();

        $name = 'delete_movies_after_uninstall';
        $new_input[$name] = ( isset( $input[$name] ) && $input[$name] == '1' ) ? '1' : '0';
        return $new_input;
    }

Display the section and field

Implement the two callbacks to separately display the section and the filed we added.

Focus on the display_field() function and try to get to know how we display the field with the information we set when adding the filed, and how to get the old value of the option.

    function display_field( $args ) {
        // Get the value of the setting we've registered with register_setting()
        $options = get_option( 'gcmovie_options' );

        ?>
        <label for="<?php echo esc_attr( $args['label_for'] ); ?>">
            <input id="<?php echo esc_attr( $args['label_for'] ); ?>" name="gcmovie_options[<?php echo esc_attr( $args['label_for'] ); ?>]" type="checkbox" value="1" <?php echo ( isset( $options[$args['label_for']] ) && $options[$args['label_for']] == "1" )  ? 'checked' : ''; ?>>
            <?php _e( 'Delete moives after uninstall', 'gcmovie' ) ?>
        </label>
        <?php
    }

Display the settings page

Add our callback to display the settings page. In this function, we check the user’s capabilities, handle error messages, and then call three functions to show the the page:

  • settings_fields(), output some hidden input fields, like option_page, action, nonce.
  • do_settings_sections(), output sections and fields added to the page.
  • submit_button(), output a submit button, with provided text and appropriate class(es).
    function show_page() {
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        // Add error/update messages

        // Check if the user have submitted the settings,
        // WordPress will add the "settings-updated" $_GET parameter to the url
        if ( isset( $_GET['settings-updated'] ) ) {
            // Add settings saved message with the class of "updated"
            add_settings_error( 'gcmovie_settings', 'gcmovie_message', __( 'Settings Saved', 'gcmovie' ), 'updated' );
        }

        // Show error/update messages
        settings_errors( 'gcmoive_settings' );
        ?>

        <div class="wrap">
            <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
            <form action="options.php" method="post">
        <?php
        settings_fields( 'gcmovie_settings' );              // Output security fields for the registered setting "gcmovie_settings".
        do_settings_sections( 'gcmovie_settings' );         // Output setting sections and their fields registed before.
        submit_button( __( 'Save Settings', 'gcmovie' ) );  // Output save settings button.
        ?>
            </form>
        </div>
        <?php
    }

Define init mthod and initiate an instance

Define init method to initiate an instance of GCMoview_Setting and call it at the end of the file outside GCMovie_Shortcode class.

class GCMovie_Setting {
    //...
    static function init() {
        new GCMovie_Admin;
    }
    //...
}

GCMovie_Setting::init();

Include the file in admin file

We organize the settings as part of GC_Admin, so include the file into it. In class-gcmovie-admin.php file, add below code to the constructor function of GCMovie_Admin class. Therefore our setting code will take effect.

        require_once GCMOVIE_PLUGIN_DIR . 'inc/class-gcmovie-setting.php';   
        $gcmovie_setting = new GCMovie_Setting;        
        add_action( 'admin_menu', array( $gcmovie_setting, 'register_pages' ) );

Now the constructor looks like:

    function __construct() {
        add_filter( 'manage_gcmovie_posts_columns', array( $this, 'custom_table_columns' ) );
        add_action( 'manage_gcmovie_posts_custom_column' , array( $this, 'custom_table_column' ), 10, 2 );  

        require_once GCMOVIE_PLUGIN_DIR . 'inc/class-gcmovie-setting.php';   
        $gcmovie_setting = new GCMovie_Setting;        
        add_action( 'admin_menu', array( $gcmovie_setting, 'register_pages' ) );
    }

Resources