WordPress Option Fields with Multiple Values

WordPress option fields

Many of you have already faced a question – “how to create a custom settings page in WP administration area?”, and Google is pleased to offer a dozen of good articles that clearly explain how to declare a list of custom fields on a separate (additional) page in admin area. But what if we need several different links. How to do this?

For example, these fields could be:

  • with the “copyright” text;
  • mailing address;
  • phone number;
  • text for you custom HTML-block;
  • social links.

And just by the example of the last item in the list a solution for the question in this article – additional options page with social links, will be considered.

The number of links may be a dozen or more. And placing the options page all the ten fields at once (facebook, google+, twitter, Instagram, etc.) will be at least not rationally – because not all these fields are necessary for our website. And what if we need two different links on Facebook or more? jQuery + WordPress Settings API will be used as the solution. The implementation will consist of the following things: from a list of known social networks you can select the desired option with the social network name and add an instance for referring to the settings form. It will look something like this:

Facebook field

Within this article we’ll not go into details of working with WP Settings API, you can read the official documentation and examples here. We used the example 2 of this source to build a page with social network URL fields.

A class for our page with social network URL fields looks the following way:

<?php
/**
* WP Theme Options
*
* @link http://codex.wordpress.org/Shortcode_API
*
* @package WordPress
* @subpackage WP-Theme
*/
/**
* Class WPThemeSettingsPage
*/
class WPThemeSettingsPage {
  /**
   * Holds Social profiles. You can add more in __construct() function.
   *
   * @var array
   */
  public $social = array();
  /**
   * Holds the values to be used in the fields callbacks
   *
   * @var $options
   */
  private $options;
  /**
   * Start up
   */
  public function __construct() {
     $this->social = array(
        'facebook' => __( 'Facebook' ),
        'twitter' => __( 'Twitter' ),
        'googleplus' => __( 'Google+' ),
        'instagram' => __( 'Instagram' ),
        'linkedin' => __( 'LinkedIn' ),
     );
     add_action( 'admin_menu', array( $this, 'add_plugin_page' ) );
     add_action( 'admin_init', array( $this, 'page_init' ) );
  }
  /**
   * Add options page
   */
  public function add_plugin_page() {
     // This page will be under "Settings".
     add_theme_page(
        __( 'Theme Options' ),
        __( 'Theme Options' ),
        'manage_options',
        'theme_options',
        array( $this, 'create_theme_options_page' )
     );
  }
  /**
   * Options page callback
   */
  public function create_theme_options_page() {
     // Set class property.
     $this->options = array( 'wp_social_profiles' => get_option( 'wp_social_profiles' ) ); ?>
     <div class="wrap">
        <!-- <h2>My Settings</h2> -->
        <form method="post" action="options.php">
           <?php
           // This prints out all hidden setting fields.
           settings_fields( 'wp_options_group' );
           do_settings_sections( 'theme_options' );
           submit_button();
           ?>
        </form>
     </div>
  <?php
  }
  /**
   * Register and add settings
   */
  public function page_init() {
     register_setting(
        'wp_options_group', /* Option group */
        'wp_social_profiles', /* Option name */
        array( $this, 'sanitize_profiles' ) /* Sanitize */
     );
     add_settings_section(
        'setting_section_id', /* ID */
        __( 'WP Theme Options' ), /* Title */
        array( $this, 'print_section_info' ), /* Callback */
        'theme_options' /* Page */
     );
     add_settings_field(
        'wp_social_profiles', /* ID */
        __( 'Social Profiles' ), /* Title */
        array( $this, 'social_profile_callback' ), /* Callback */
        'theme_options', /* Page */
        'setting_section_id' /* Section */
     );
  }
  /**
   *     /**
   * Sanitize each setting field as needed.
   *
   * @param array $input Contains all settings fields as array keys.
   * @return array
   */
  public function sanitize_profiles( $input ) {
     $new_input = array();
     // Sanitize Social Profiles values.
     foreach ( (array) $input as $name => $element ) {
        foreach ( $element as $index => $value ) {
           if ( ! empty( $value ) ) {
              $new_input[ $name ][ $index ] = esc_url( $value );
           }
        }
     }
     return $new_input;
  }
  /**
   * Print the Section text
   */
  public function print_section_info() {
     esc_html_e( 'Enter your settings below:' );
  }
  /**
   * Get the settings option array and print one of its values
   */
  public function social_profile_callback() {
     if ( ! empty( $this->options['wp_social_profiles'] ) ) {
        foreach ( (array) $this->options['wp_social_profiles'] as $name => $element ) {
           foreach ( $element as $index => $value ) { ?>
              <div class="wp-social-profile">
                 <label for="wp_social_profiles_<?php echo esc_attr( $name ); ?>_<?php echo esc_attr( $index ); ?>" class="wp-option-label">
                    <?php echo esc_html( $this->social[ $name ] ); ?>:
                 </label>
                 <input
                    type="text"
                    id="wp_social_profiles_<?php echo esc_attr( $name ); ?>_<?php echo esc_attr( $index ); ?>"
                    name="wp_social_profiles[<?php echo esc_attr( $name ); ?>][]"
                    class="<?php echo esc_attr( $name ); ?>"
                    value="<?php echo esc_attr( $value ); ?>"
                    placeholder="<?php esc_attr_e( 'http://' ); ?>"
                 />
                 <button class="button wp-social-remove"><b>–</b></button>
              </div>
              <?php
           }
        }
     } else { ?>
        <div class="wp-social-profile">
           <label for="wp_social_profiles_facebook_1" class="wp-option-label"><?php echo esc_html( $this->social['facebook'] ); ?>:</label>
           <input
              type="text"
              id="wp_social_profiles_facebook_1"
              name="wp_social_profiles[facebook][]"
              class="facebook"
              value=""
              placeholder="<?php esc_attr_e( 'http://' ); ?>"
           />
           <button class="button wp-social-remove">-</button>
        </div>
        <?php  } ?>
     <hr>
     <div class="wp-social-profile-selector-wrapper">
        <label for="social_profile_selector" class="wp-option-label"><?php esc_attr_e( 'Select profile: ' ); ?></label>
        <select id="social_profile_selector">
           <?php
           foreach ( $this->social as $name => $option ) { ?>
              <option <?php selected( $name, 'facebook' ); ?> value="<?php echo esc_attr( $name ); ?>"><?php echo esc_html( $option ); ?></option>
           <?php } ?>
        </select>
        <button id="social_profile_add" class="button">Add new...</button>
     </div>
     <?php
  }
}
if ( is_admin() ) {
  $settings_page = new WPThemeSettingsPage();
}
function load_option_page_style() {
  wp_register_script( 'wptheme-options-script', get_template_directory_uri() . '/inc/js/theme_options.js', array( 'jquery' ), '1.0.0', true );
  wp_enqueue_script( 'wptheme-options-script' );
}
add_action( 'admin_enqueue_scripts', 'load_option_page_style' );

And JS, which allows us to add/ remove fields:

// Remap jQuery to $.
(function ($) {
  // Re-index profiles.
  function RefreshProfilesIndex(selector) {
     $(document).find("[id^=social_profile_" + selector + "]").each(function (index) {
        $(this).attr('id', 'social_profile_' + selector + '_' + (index));
        $(this).closest('div').find('label').attr('for', 'social_profile_' + selector + '_' + (index));
     });
  }
  // Capitalize first letter on string.
  function toTitleCase(str) {
     return str.replace(/wS*/g, function (txt) {
        return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
     });
  }
  // Update Events.
  function RefreshEventListener() {
     // Remove handler from existing elements
     $("button.wp-social-remove").off();
     // Re-add event handler for all matching elements
     $("button.wp-social-remove").on("click", function (event) {
        event.preventDefault();
        var selected = $(event.target).parent('div').find('input').attr('class');
        $(event.target).parents('div.wp-social-profile').css('visibility', 'hidden').slideUp("normal", function () {
           $(this).remove();
           RefreshProfilesIndex(selected);
        });
     });
  }
  // Select options toggle refresh.
  function RefreshSelectOptions(target_id) {
     if (target_id === undefined) {
        var $target = $(document).find("select.select-toggle option");
     } else {
        var $target = $(document).find("#" + target_id).closest("form").find("select.select-toggle option");
     }
     $target.on("mousedown", function () {
        var $self = $(this);
        if ($self.prop("selected"))
           $self.prop("selected", false);
        else
           $self.prop("selected", true);
        return false;
     });
  }
  /* trigger when page is ready */
  $(document).ready(function () {
     RefreshEventListener();

     $("#social_profile_add").on("click", function (event) {
        event.preventDefault();

        var selected = $("#social_profile_selector").val();
        var count = parseInt($(document).find("[id^=wp_social_profiles_" + selected + "]").length);
        var $clone = $(document).find(".wp-social-profile").first().clone();

        $clone = $('<div>').addClass("wp-social-profile");
        $clone.html(
           '<label for="wp_social_profiles_' + selected + '_1" class="wp-option-label">' + toTitleCase(selected) + ':</label>' +
           '<input ' +
              'type="text" id="wp_social_profiles_' + selected + '_1" ' +
              'name="wp_social_profiles[' + selected + '][]" ' +
              'class="' + selected + '" ' +
              'value="" ' +
              'placeholder="http://" />' +
           '<button class="button wp-social-remove"><b>-</b></button>');
        $clone.insertBefore($(document).find(".wp-social-profile-selector-wrapper").prev()).hide().css({visibility: 'hidden'}).slideDown("normal", function () {
           $(this).css('visibility', 'visible');
        });
        RefreshEventListener();
     });
     RefreshSelectOptions();
  });
  $(".widget-control-save").on("click", function (event) {
     setTimeout(function () {
        RefreshSelectOptions(event.target.id)
     }, 500);
  });
}(window.jQuery || window.$));

Let’s consider the code shown above.

As you can see, the fields are stored as an associative array where the links are grouped by social network title. To let the WP Settings API preserve values from input in the form as array, you should specify the attribute name using the following format:

<input name=wp_social_profiles[facebook][] value=”http://facebook.com/” />
<input name=wp_social_profiles[twitter][] value=”http://twitter.com/” />
<input name=wp_social_profiles[twitter][] value=”http://twitter.com/helloworld” />
<input name=wp_social_profiles[googleplus][] value=”http://googleplus.com/” />
<input name=wp_social_profiles[linkedin][] value=”http://linkedin.com/” />

The key point is the suffix [] in the attribute name. After submitting the form setting with fields in the format, as above, we get an associative array from function get_options( “wp_social_profiles”), such as in the picture above.

If we consider our settings page class more carefully, we declare a “social” array in the construct method:

$this->social = array(
  'facebook' => __( 'Facebook' ),
  'twitter' => __( 'Twitter' ),
  'googleplus' => __( 'Google+' ),
  'instagram' => __( 'Instagram' ),
  'linkedin' => __( 'LinkedIn' ),
);

We’ll route the array with a frontage circle that’s why it could be added among the other social networks. The format is the following: the key of the array element is something like a slug of the social network, and the element value is a header field which we use to display the Preferences page:

Preferences Page

JS-script shown in the example above implements the events: delete, and create a new DOM-element on the form, and recalculates the id and for attributes for label and input elements.

It was a quick tip, and we’ll be happy if it’s useful for you. If you have any difficultness with it, contact us and we’ll make it work. Save your time and trust us your WordPress based website!

5.00 / 5.0
Article rating (0 Reviews)
Do you find this article useful? Please, let us know your opinion and rate the post!
Not badGoodVery GoodGreatAwesome

Do you have a question?

Or do you want to talk and share some news? We’ll be glad to communicate with you and clarify all you need. Feel free to contact us anytime, and we’ll reply as soon as possible. Let’s get acquainted and be partners. We’ll be happy to keep in touch with you.

We accept: DOCX, DOC, ODT, PDF

* - required fields