WordPress Plugin Development Tutorial #2

In the first part of this WordPress plugin tutorial, we covered some of the basics of WordPress plugins. We briefly discussed the WordPress plugin Boilerplate. In this part we want to create a settings page for the plugin. In order to be able to follow the tutorial better, we provide you with the source code in this Git repository. We will continue to develop this repository with future posts in this series.

⇒ Here you go directly to Part #1 and Part #3 of the WordPress Plugin Development Tutorial.

What is a settings page?

A settings page is a view that can be used to make predefined settings.
For this purpose, a form is used in WordPress that sends its fields to options.php from the WordPress framework when it is sent. Here the field values ​​are received and a validation process is started. The validated fields are then recorded in a new entry in the wp_options table.
Alternatively, you could also create your own form in HTML and output it. To do this, simply specify “options.php” as the form action. However, if you try this, you will quickly find that the settings are not persisted in the database. You must first register your settings with WordPress and provide a validation callback. The register_setting function from the Settings API is used for this. This API offers you even more functions, the use of which makes the creation of settings pages easier and also more secure.

The WordPress Settings API

WordPress provides a whole collection of features to unify the creation of settings pages and abstract many of the processes. There are a number of benefits to using these features, which we’ll start with here.

Unification: The functions create standardized structures that are already known from the WordPress backend. Additionally, standard WordPress input validations are applied automatically.

time saving: Instead of creating and styling your own form, you can create complex settings forms using a few functions.

Security: Input fields always involve a small risk, since they represent a simple interface to the server. There are many rudimentary attack strategies that try to trigger scripts on the server via free text fields. In the WordPress core, filter functions are therefore applied to all inputs that filter out such unsafe inputs. The Settings API offers simple ways to validate and clean the inputs.

settings structure

The Settings API uses three structures to manage settings: Fields, Sections, and Settings. These are hierarchical and allow the options to be grouped sensibly.

Settings: The settings group a collection of individual settings into a coherent framework. It is the top level of the Settings API. At the same time, they represent the reference for the settings. As a rule, there is one settings entry per settings page.

sections: The sections represent a logical or semantic grouping of settings. They are visually distinguished from one another and can be nested within one another to structure the form. Each settings page has at least one section.

Fields: The fields are the individual input fields and are assigned to a section. At the same time, they represent the individual values ​​of the settings.

Using the Settings API

Now we got a small overview of the Settings API and want to use it in our plugin. Let’s stay with the example of a slider for posts with custom content types. First of all, you should think about which settings the user of our plugin should be able to make.
As input fields we want to provide a text field for the content type, a checkbox for auto slide and a text field for the timing of auto slide. These are our fields.
Now we group these fields logically and thus define the sections. In this case it makes sense to assign the two fields for automatic sliding to a group. In this way, the text field for the content type gets its own section.

Create a plugin options page

Before we add the settings, let’s first register an options page with WordPress. There are several places to add an options page. In this case we limit ourselves to a subpage of the plugin menu. For this we use the add_plugins_page function and add two methods in the Pm_Example_Admin class (/admin/class-pm-example-admin.php).

/**
 * Register the settings page in admin area.
 *
 * @since    1.0.0
 */
public function add_settings_page(){
  add_plugins_page(
    'The P&M example settings page',
    'P&M Example Settings',
    'manage_options',
    $this->plugin_name,
    array( $this, 'display_settings_page' ) );
}
/**
 * Display the settings page in admin area.
 *
 * @since    1.0.0
 */
public function display_settings_page(){
  ?>
  <div>
    <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
    <?php settings_errors(); ?>
    <form action="options.php" method="post">
    </form>
  </div>
  <?php
}

The display_settings_page method is used here to create a basic HTML framework for the settings page. We’ll have to add the settings fields to this later. The wrap class ensures that all elements of the form receive the familiar display from the WordPress backend.

If you now reload the backend, you will notice that the options page does not appear in the admin sidebar under the Plugins item. That’s because WordPress hasn’t yet received a prompt to call that method as well. For this we have to use a so-called hook. Hooks are predefined entry points to which functions can be bound. We’ll go into more detail about hooks in a later part of this series. For example, the admin_menu hook is used to generate the admin menu. This can be used to expand the admin sidebar before it is fully generated. The Boilerplate plugin provides the define_admin_hooks method in the Pm_Example class (/includes/class-pm-example.php). We add the following line to this:

$this->loader->add_action( 'admin_menu', $plugin_admin, 'add_settings_page' );

Now, when building the admin sidebar, our options page is registered and should now also appear as a subpage in the plugins menu item. Next, we can create the settings using the Settings API.

Creation of settings

First of all, we define a new method add_settings in the admin class, which we use to handle the registration of sections, fields and settings. In this we first want to create an options entry in the database and register the settings.

/**
 * Register all the settings, fields and sections.
 *
 * @since    1.0.0
 */
public function add_settings(){
  // Create the option first if not done already.
  add_option( 'pm_example_settings_group' );
  // register settings in WP
  register_setting(
    'pm_example_settings_group',
    'pm_example_settings_name'
  );
}

By calling add_option we create an entry for the options group pm_example_settings_group in the settings database. We can then assign Sections and Fields to this group. The call to register_setting assigns an option named pm_example_settings_name to the group.

In order for this method to be called, we have to use a hook again. In this case we use admin_init. This is always applied when the backend is loaded. The define_admin_hooks now looks like this:

private function define_admin_hooks() {
    $plugin_admin = new Pm_Example_Admin( $this->get_plugin_name(), $this->get_version() );
    $this->loader->add_action( 
'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
    $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
    $this->loader->add_action( 'admin_menu', $plugin_admin, 'add_settings_page' );
    $this->loader->add_action( 'admin_init', $plugin_admin, 'add_settings' );
  }

Creation of sections

Now that we’ve created the options, let’s start adding a Section. The add_settings_section function is used for this. We call this in the add_settings method of the admin class after the options group has been registered.

public function add_settings(){
    // Create the option first if not done already.
    add_option( 'pm_example_settings_group' );
    // add settings section for post-type
    add_settings_section(
      'pm_example_posttype_section',
      'Inhaltstyp',
      array( $this, 'render_settings_section' ),
      'pm_example_settings_group'
    );
    // register all sections and fields in WP
    register_setting(
      'pm_example_settings_group',
      'pm_example_settings_name'
    );
}

The first parameter represents a reference to the section, the second parameter is the title of the section. The array of the third parameter is used as a callback to create additional content between the heading and the fields of the section. An array is used here to reference an object method. This method must therefore be added to the admin class and can remain empty for this example. The last parameter assigns the section to an option group.

So that our section is now also displayed, we have to extend the display_settings_page method a little and load the registered settings there.

/**
 * Display the settings page in admin area.
 *
 * @since    1.0.0
 */
public function display_settings_page(){
  ?>
  <div>
    <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
    <?php settings_errors(); ?>
    <form action="options.php" method="post">
      <?php
      settings_fields( 'pm_example_settings_group' );
      do_settings_sections( 'pm_example_settings_group' );
      submit_button( 'Save Settings' );
      ?>
    </form>
  </div>
  <?php
}

Creation of fields

The section created in this way can now be used to group and display settings fields. The Settings API provides the add_settings_field function for creating a field.

// add the field for a post-type input
add_settings_field(
  'slider_posttype',
  'Inhaltstyp für den Slider',
  array( $this, 'render_text_input' ),
  'pm_example_settings_group',
  'pm_example_posttype_section',
  array(
    'option'    => 'slider_posttype'
  )
);

This function is almost identical to the add_settings_section, but offers two additional parameters. The first new parameter represents an assignment to a section (here with the value ‘pm_example_posttype_section’). The last parameter allows the field rendering function to announce additional data in the form of an array. Here we pass the option name to allow a generic function for creating text inputs.

/**
 * Renders input for a text field.
 *
 * This uses the parameter exposed to the callback of add_settings_field to get the option name.
 *
 * @since    1.0.0
 */
public function render_text_input( $args ) {
  $options = get_option( 'pm_example_settings_name' );
  $value = ( isset( $options[ $args['option'] ] )? $options[ $args['option'] ] : '' );
  $html="<input type="text" id="pm_example_settings_name[". $args['option'] .']" name="pm_example_settings_name['. $args['option'] .']" value="'. $value .'"/>';
  echo $html;
}

The function for creating the text input field first pulls the previously saved settings via get_option. In our case, this is an associative array with the field name as the key. We get the field name from the $args array using the ‘option’ key. Now we can check if there is already a value for the field and then create an input field. The name must be specified here so that the reference to the registered settings group and the corresponding field can be established.

Wordpress Plugin Development - Sample Settings Page

summary

We’ve created our first working settings panel and can access the settings as well. You can find the source code for the second section and the missing fields in our repository. In this part, we deliberately avoided using more complex input elements and our own validation method. In a later part of this series we will come back to the settings page and refine it. First, however, we will continue to familiarize ourselves with the WordPress framework and further develop our example plugin.

outlook

In part 3 of our series we will deal with the posttypes, the most important data structure of WordPress, and also create our own posttypes.

About the author

Waldemar Schiller

Waldemar Schiller started his career as an intern at P&M and quickly rose to Senior Developer. His major weakness is M&Ms, but we won’t reveal that.