The other day I was wondering how I could let WordPress plugin options be controlled by a JSON file, allowing one to more easily add additional settings in the future without having to adjust the code itself.
This article provides an example of an extremely simple WordPress plugin with a single settings pages consisting of 2 sections and 3 fields / options.
The full code can be found on Github.
Setting up the base
The plugin initially consists of 3 files.
- adventures.json
- adventures.php
- class.adventures.php
adventures.php
with a basic plugin registration:
<?php
/*
Plugin Name: Adventures
Plugin URI: https://mortenhartvig.dk
Description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pharetra nisi eu varius pellentesque. Aenean posuere, velit mollis sodales convallis, ipsum lectus feugiat nunc, ac auctor sapien enim eu metus.
Version: 1
Requires at least: 6.1
Requires PHP: 8.3
Author: Morten Hartvig
Author URI: https://mortenhartvig.dk
License: Do whatever you want
*/
define('ADV__PLUGIN_DIR', plugin_dir_path(__FILE__));
define('ADV__PLUGIN_VIEW', ADV__PLUGIN_DIR . 'views');
define('ADV__PLUGIN_SLUG', 'adv');
require_once ADV__PLUGIN_DIR . 'class.adventures.php';
(new Adventures());
class.adventures.php
with an empty class:
<?php
class Adventures {
public function __construct() {
}
}
adventures.json
contains the JSON structure of the plugin settings:
{
"settings": {
"pages": [
{
"title": "Adventures",
"capability": "manage_options",
"slug": "adv"
}
],
"sections": [
{
"id": "portal_base",
"title": "Base configuration",
"description": "Lorem 1, ipsum dolor sit amet consectetur adipisicing elit. Cumque nulla in officiis. Laborum quisquam illo eaque, deserunt facere mollitia sint doloremque maiores, obcaecati reiciendis voluptate itaque iure fugiat quia architecto!",
"view": "section"
},
{
"id": "portal_appearance",
"title": "Appearance",
"description": "Lorem 2, ipsum dolor sit amet consectetur adipisicing elit. Cumque nulla in officiis. Laborum quisquam illo eaque, deserunt facere mollitia sint doloremque maiores, obcaecati reiciendis voluptate itaque iure fugiat quia architecto!",
"view": "section"
}
],
"fields": [
{
"id": "adv_portal_key",
"title": "Portal Key",
"section": "portal_base",
"type": "text",
"placeholder": "Enter your portal key",
"view": "field.text"
},
{
"id": "adv_api_host",
"title": "Host API",
"section": "portal_base",
"type": "text",
"placeholder": "Enter API host",
"default": "https://api.mortenhartvig.dk",
"view": "field.text"
},
{
"id": "adv_portal_theme",
"title": "Theme",
"section": "portal_appearance",
"type": "select",
"options": {
"rounded.v1": "Round (V1)",
"squared.v1": "Square (V1)",
"standard": "Standard"
},
"default": "standard",
"view": "field.select"
}
]
}
}
Reading JSON data
Create a property for your settings and call set_settings()
in ´class.adventures.php´:
private $settings;
public function __construct() {
$this->set_settings();
}
Create set_settings()
and get_json_data()
:
private function set_settings() {
$data = $this->get_json_data();
$this->settings = $data['settings'];
}
private function get_json_data() {
$file = ADV__PLUGIN_DIR . 'adventures.json';
if (!file_exists($file)) {
die('adventures.json not found');
}
return json_decode(file_get_contents($file), true);
}
If you add a die(print_r($this->settings))
to your __construct
you can confirm that you indeed have your settings loaded.
Settings page
Call init_hooks()
from the constructor:
public function __construct() {
$this->set_settings();
$this->init_hooks();
}
Create init_hooks()
:
private function init_hooks() {
add_action('admin_menu', [$this, 'register_settings_pages']);
}
Create register_settings_pages()
and settings_page_callback()
.
public function register_settings_pages() {
foreach ($this->settings['pages'] as $page) {
add_options_page($page['title'], $page['title'], $page['capability'], $page['slug'], [$this, 'settings_page_callback']);
}
}
public function settings_page_callback() {
$this->render('settings.php');
}
Create render()
:
private function render($filename, $args) {
if (is_array($args)) {
$value = get_option($args['id']);
if (empty($value) && isset($args['default'])) {
$value = $args['default'];
}
$args = array_merge($args, ['value' => $value]);
}
$file = ADV__PLUGIN_VIEW . $filename;
if (!str_ends_with($file,'.php')) {
$file .= '.php';
}
if(!file_exists($file)) {
die('File not found ' . $filename);
}
require $file;
}
Create views/settings.php
:
<div class="wrap">
<h1>Adventures</h1>
</div>
Sections
Add another action in init_hooks
:
private function init_hooks() {
add_action('admin_menu', [$this, 'register_settings_pages']);
add_action('admin_init', [$this, 'register_settings_sections']);
}
Create register_settings_sections()
and settings_section_callback()
:
public function register_settings_sections() {
foreach ($this->settings['sections'] as $section) {
add_settings_section($section['id'], $section['title'], [$this, 'settings_section_callback'], ADV__PLUGIN_SLUG, $section);
}
}
public function settings_section_callback($args) {
$this->render($args['view'], $args);
}
Create views/section.php
:
<p>
<?php echo $args['description']; ?>
</p>
Fields
Add a third action in init_hooks
:
private function init_hooks() {
add_action('admin_menu', [$this, 'register_settings_pages']);
add_action('admin_init', [$this, 'register_settings_sections']);
add_action('admin_init', [$this, 'register_settings_fields']);
}
Create register_settings_fields()
and settings_field_callback()
:
public function register_settings_fields() {
foreach ($this->settings['fields'] as $field) {
add_settings_field($field['id'], $field['title'], [$this, 'settings_field_callback'], ADV__PLUGIN_SLUG, $field['section'], $field);
register_setting(ADV__PLUGIN_SLUG, $field['id']);
}
}
public function settings_field_callback($args) {
$this->render($args['view'], $args);
}
Create views/field.select.php
:
<?php
$html_options = '';
foreach ($args['options'] as $key => $val) {
$html_options .= sprintf('<option value="%s" %s >%s</option>', $key, selected($args['value'], $key, false), $val);
}
printf('<select name="%s" id="%s">%s</select>', $args['id'], $args['id'], $html_options);
Create views/field.text.php
:
<?php
printf('<input name="%s" id="%s" type="text" value="%s" placeholder="%s" />',
$args['id'], $args['id'], $args['value'], $args['placeholder']);
Saving
To view and save your options add a form to settings.php
:
<div class="wrap">
<h1>Adventures</h1>
<form action="options.php" method="post">
<?php
settings_fields(ADV__PLUGIN_SLUG);
do_settings_sections(ADV__PLUGIN_SLUG);
submit_button();
?>
</form>
</div>
Change an option and attempt to Save. The save should be successful.
Your settings are now saved and can be accessed throughout the site via:
<?php
echo get_option('adv_portal_theme'); // squared.v1
Adding new fields
Add the JSON below to adventures.json
:
{
"id": "adv_api_token",
"title": "Host API Token",
"section": "portal_base",
"type": "text",
"placeholder": "Enter API host token",
"default": "",
"view": "field.text"
}
It will automatically be added to your settings:
Top comments (0)