Another Strategy for Initializing Plugin Settings
- Author:
- Date: Posted on
- In: Development, General Topics, WordPress
Tom McFarlin wrote an interesting article titled A Strategy for Initializing Plugin Settings. In it he suggested that there were three ways to save and retrieve plugin settings in WordPress:
use the Settings API,
use a custom page and calls that are a hybrid of the two,
use some type of hybrid between a submenu page (or a menu page) and other functionality.
Tom McFarlin
I’d like to throw my hat into the ring and offer yet another alternative to accomplish this, and I believe it to be a cleaner and more efficient way.
Use the built-in WordPress Customizer, also known as the Customize API. It’s incredibly easy to add settings into the WordPress customizer, and you will future-proof your plugins. In Gutenberg Phase 2, blocks will be brought into the customizer.
The next phase of Gutenberg will take the ease of use that was introduced with block-based editing and extend that capability to the Customizer, starting with widgets, to make it easier for users to customize the rest of their sites.
In the first step of phase 2, we will bring blocks into the Customizer to create a more consistent user experience and give developers a smoother path to upgrading themes to Gutenberg. Longer term, we will expand the current Gutenberg post and page editor to become a full-fledged site editor, bringing everything together into a unified, modern product experience.
Mel Choyce
The customizer is here to stay, and from the looks of it, it will be an essential part for the future of Gutenberg and therefore WordPress.
I know what you’re thinking. Isn’t the customizer just for themes?
No.
When creating a customizer settings object, there are two main types of settings – theme modifications (mods), and options.
Theme mods are the default when storing settings data. Theme mods are tied to a specific theme, and do not transfer over or are not linked to the same settings when switching themes. To call a theme mod into a theme, you would use get_theme_mod().
The solution for plugins is to use options. Options store in the wp_options
table directly, and can apply regardless of which theme is activated. To call an option into a plugin, simply use get_option().
Customizer settings already handle the saving, and sanitization of customizer objects. Here is an example of adding a customizer setting with options specified:
$wp_customize->add_setting('custom_plugin_setting', array( 'type' => 'option' ));
You also have the ability to use a keyed array for options, to allow for storing multiple settings into one option. For example:
$wp_customize->add_setting('custom_plugin_settings[color]', array( 'type' => 'option', 'default' => '#000000' )); $wp_customize->add_setting('custom_plugin_settings[limit]', array( 'type' => 'option', 'default' => '20' ));
Initializing Plugin Settings
Like Tom, I also prefer to use classes in my plugins and themes. I typically would initialize plugin settings in a core plugin class. I’ll give you an example of what I do, and then go into the explanation below:
class MyPlugin { public static $settings = array(); private function __construct() { // set settings self::$settings = array( 'setting' => array( 'name' => 'get_bloginfo', 'admin_email' => 'get_bloginfo', 'phone' => 'get_option', 'email' => 'get_option', 'facebook_url' => 'get_option', 'google_plus_url' => 'get_option', 'twitter_url' => 'get_option', 'youtube_url' => 'get_option', 'linkedin_url' => 'get_option', 'pinterest_url' => 'get_option', 'instagram_url' => 'get_option', 'flickr_url' => 'get_option', 'rss_url' => 'get_option' ) ); } // get a setting from the settings array public static function setting($setting = FALSE) { $output = FALSE; if (!$setting) { return $output; } // set function variable $function = self::$settings['setting'][$setting]; // if setting not already loaded, set it so we're more efficient on the next call if (!array_key_exists($setting, self::$settings)) { self::$settings[$setting] = $function($setting); } // set output to setting $output = self::$settings[$setting]; return $output; } }
My approach is with load time and versatility in mind. I want to standardize the calls that would need to be made, regardless of whether it’s an option or a theme mod or get_bloginfo(). I also want to only call what I need, and to limit the number of database queries when calling the same data multiple times on a single page load.
As you can see in the constructor, I set a static variable settings array with a predefined set of settings names as the key, and the corresponding function as the element for retrieving that setting. The setting name must match that of the actual setting regardless of the type.
This is what will allow for me to simply call MyPlugin::setting('facebook_url')
instead of get_option('facebook_url')
or get_bloginfo('admin_email')
. In this way, I only need to know the setting I want to get, and call the static method for getting that setting without bothering with what type of setting it’s supposed to be.
Running through the setting()
method:
- I set an output variable to
FALSE
as a default. - I check to see if the
$setting
argument was set, and if not, return the output variable (which isFALSE
). - I set a function variable from the static settings variable array using the setting argument as the key.
- Then, I check if the static variable settings array contains an existing key that matches the setting argument. If not, then we will set it by calling the function variable and passing the setting argument through the function variable.
- Then we set the output variable to the static variable setting and return it.
In the case that step #4 is TRUE
we simply reference the existing value in the static variable settings array, thereby eliminating the need to make additional database queries. If it’s FALSE
, then we make a single database query, and then set the setting value in the array for future use.
If I were to implement Tom’s approach of storing all option settings in a single array based option, that would reduce the number of possible database calls, but would require rewriting the setting()
method.
Tell me what you think. What would you do differently? Leave me a comment below.