Skip to content

Instantly share code, notes, and snippets.

@zacscott
Last active March 11, 2017 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zacscott/79a27afead4f12cd0a83 to your computer and use it in GitHub Desktop.
Save zacscott/79a27afead4f12cd0a83 to your computer and use it in GitHub Desktop.
Settings builder utility for WordPress (can be used as a mu-plugin)
<?php
add_action( 'plugins_loaded', function() {
$settings = new zacscott\SettingsBuilder();
$settings->page( array(
'title' => 'TEST ',
'menu' => 'Test Settings',
) );
$settings->section( array(
'title' => 'Test Settings',
) );
$settings->text( array(
'label' => __( 'Textual option', 'text_domain' ),
'option' => 'text_op',
) );
$settings->number( array(
'label' => __( 'Number option', 'text_domain' ),
'option' => 'number_op',
) );
$settings->bool( array(
'label' => __( 'Boolean option', 'text_domain' ),
'option' => 'bool_op',
) );
$settings->select( array(
'label' => __( 'Select options', 'text_domain' ),
'option' => 'select_op',
'vals' => array(
'test1' => 'Test 1',
'test2' => 'Test 2',
'test3' => 'Test 3',
)
) );
$settings->build();
} );
<?php
/*
* Plugin Name: Settings Builder
* Description: Simple WP settings builder API
* Version: 1.0
* Author: Zachary Scott
*/
namespace zacscott;
/**
* A single settings field in a Section. Used internally by Section.
*
* @author Zachary Scott <zscott.dev@gmail.com>
*/
class Field {
public $args = array();
public function __construct( $args ) {
assert( ! empty( $args ) );
$this->args = $args;
}
/**
* Registers the settings field with WordPress.
* Must be called within 'admin_init' hook.
*
* @param $id The ID of the setting field.
* @param $page The Page object which this field belongs to.
*/
public function build( $id, $page ) {
assert( ! empty( $id ) );
assert( ! empty( $page ) );
if ( 'hr' !== (string) $this->type ) { // dont register setting for <hr>
// register the option setting
register_setting(
$page,
$this->option,
array( $this, 'validate' )
);
}
add_settings_field(
$this->option,
$this->label,
array( $this, $this->type ),
$page,
$id,
array( // args to callback
$this->vals
)
);
}
// Validates the setting field.
function validate( $value ) {
$validate = $this->validate;
if ( ! empty( $validate ) ) {
$err = call_user_func( $validate, $value );
// report error - if any
if ( ! empty( $err ) ) {
add_settings_error( $this->option, $this->option, $err );
return get_option( $this->option ); // failed - revert to previous value
}
}
return $value;
}
// Output <input type="checkbox"/>
function bool() {
assert( 'bool' === (string) $this->type );
$val = get_option( $this->option );
$name = $this->option;
echo "<label for='". esc_attr( $name ) ."'>";
echo "<input type='checkbox' id='". esc_attr( $name ) ."' name='". esc_attr( $name ) ."' ";
echo esc_attr( $val ? "checked='checked'" : '' );
echo "/> \n";
echo esc_html( $this->placeholder );
echo '</label>';
}
// Output <input type="text"/>
function text() {
assert( 'text' === (string) $this->type );
$val = get_option( $this->option );
$name = $this->option;
$placeholder = $this->placeholder;
echo "<input type='text' name='". esc_attr( $name ) ."' ";
echo "value='". esc_attr( $val ) ."' ";
echo "placeholder='". esc_attr( $placeholder ) ."'/> \n";
}
// Output <textarea/>
function textarea() {
assert( 'textarea' === $this->type );
$val = get_option( $this->option );
$name = $this->option;
$placeholder = $this->placeholder;
echo "<textarea cols='40' rows='8' ";
echo "name='". esc_attr( $name ) ."'";
echo "placeholder='". esc_attr( $placeholder ) ."'";
echo '>'. esc_html( $val ) ."</textarea> \n";
}
// Output <input type="number"/>
function number() {
assert( 'number' === (string) $this->type );
$val = get_option( $this->option );
$name = $this->option;
$placeholder = $this->placeholder;
echo "<input type='number' ";
echo "name='". esc_attr( $name ) ."' ";
echo "value='". esc_attr( $val ) ."' ";
echo "placeholder='". esc_attr( $placeholder ) ."'/> \n";
}
// Output <select>
function select() {
assert( 'select' === (string) $this->type );
$current = (string) get_option( $this->option );
$name = $this->option;
echo '<select name="'. esc_attr( $name ) .'"> ' ."\n";
// add all available options
foreach ( $this->vals as $key => $val ) {
$key = esc_attr( $key );
$val = esc_html( $val );
// whether the current one - will be selected
$selected = ( (string) $key === $current ) ? ' selected="selected"' : '';
// output <option>
echo '<option value="'. esc_attr( $key ) .'"'. esc_attr( $selected ) .'>';
echo esc_html( $val );
echo '</option>';
echo "\n";
}
echo "</select> \n";
}
// Output <hr>
function hr() {
echo '<hr>';
}
// $args acessor
function __get( $name ) {
return isset( $this->args[ $name ] ) ? $this->args[ $name ] : '';
}
}
/**
* A settings section which contains zero or more Field's.
* Used internally by Page.
*
* @author Zachary Scott <zscott.dev@gmail.com>
*/
class Section {
private $args = array();
private $fields = array(); // all fields in the section
public $id; // unique ID of the section
public function __construct( $args ) {
assert( ! empty( $args ) );
$this->args = $args;
// Generate random ID for the section
$this->id = uniqid( '_', true );
}
/**
* Add the given field to the section.
*
* @param Field $field The field object to add.
*/
public function add( $field ) {
assert( ! empty( $field ) );
$this->fields[] = $field;
}
/**
* Registers the settings section and its fields with WordPress.
* Must be called within 'admin_init' hook.
*
* @param $page The page to which the section belongs
*/
public function build( $page ) {
assert( ! empty( $page ) );
$subtext = $this->subtext;
// add the settings section
add_settings_section(
$this->id,
$this->title,
array( $this, 'subtext' ),
$page
);
// add all section settings
foreach ( $this->fields as $field ) {
$field->build( $this->id, $page );
}
}
// Spits out the subtext for the section
function subtext() {
echo '<p>'. esc_html( $this->subtext ) .'</p>';
}
// $args acessor
function __get( $name ) {
return isset( $this->args[ $name ] ) ? $this->args[ $name ] : '';
}
}
/**
* A settings page - which contains one or more Section's. Each Section
* encapsultes zero or more `Field`s. Each field represents a WordPress option
* which can be changed.
*
* Used internally by `SettingsBuilder`.
*
* @author Zachary Scott <zscott.dev@gmail.com>
*/
class Page {
// count of all page instances for menu slug
private static $count = 1;
private $args = array();
private $slug;
private $sections; // list of settings sections on the page
public function __construct( $args ) {
assert( ! empty( $args ) );
$default_args = array(
'title' => '',
'menu' => '',
'cap' => 'manage_options',
);
$this->args = array_merge( $default_args, $args );
$this->slug = plugin_basename( dirname( __FILE__ ) ) .'-settings-'. Page::$count++;
}
/**
* Creates a new section in which to place subsequent settings.
*
* @param $title - The title of the section to display to the user.
*/
public function section( $args ) {
assert( ! empty( $args ) );
$this->sections[] = new Section( $args );
}
/**
* Creates a boolean setting field (checkbox).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function bool( $args ) {
assert( ! empty( $this->sections ) );
assert( ! empty( $args ) );
$default_args = array(
'type' => 'bool',
);
$args = array_merge( $default_args, $args );
$this->add_field( new Field( $args ) );
}
/**
* Creates a textual setting field (textbox).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function text( $args ) {
assert( ! empty( $this->sections ) );
assert( ! empty( $args ) );
$default_args = array(
'type' => 'text',
);
$args = array_merge( $default_args, $args );
$this->add_field( new Field( $args ) );
}
/**
* Creates a multi-line textarea setting field (textarea).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function textarea( $args ) {
assert( ! empty( $this->sections ) );
assert( ! empty( $args ) );
$default_args = array(
'type' => 'textarea',
);
$args = array_merge( $default_args, $args );
$this->add_field( new Field( $args ) );
}
/**
* Creates a number setting field.
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function number( $args ) {
assert( ! empty( $this->sections ) );
assert( ! empty( $args ) );
$default_args = array(
'type' => 'number',
);
$args = array_merge( $default_args, $args );
$this->add_field( new Field( $args ) );
}
/**
* Creates a list of options (combobox).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $vals The list of available options
* @param string $validate Validation callback
*/
public function select( $args ) {
assert( ! empty( $this->sections ) );
assert( ! empty( $args ) );
$default_args = array(
'type' => 'select',
);
$args = array_merge( $default_args, $args );
$this->add_field( new Field( $args ) );
}
/** Creates a <hr>. */
public function hr() {
$args = array(
'type' => 'hr',
'option' => mt_rand(),
);
$this->add_field( new Field( $args ) );
}
// Adds the given field to the page
private function add_field( $field ) {
assert( ! empty( $field ) );
$end = sizeof( $this->sections );
$this->sections[ $end - 1 ]->add( $field );
}
/**
* Registers the options page with WordPress.
* Must be called within 'admin_init' hook.
*/
public function build() {
// register the page w/ wordpress
add_action( 'admin_menu', array( $this, 'register_settings_page' ), 999999999 );
// register the settigns sections with wordpress
add_action( 'admin_init', array( $this, 'register_settings' ), 999999999 );
}
// Registers the settings page w/ WP
function register_settings_page() {
add_options_page(
$this->title,
$this->menu,
$this->cap,
$this->slug,
array( $this, 'render' )
);
}
// Registers the page settings w/ WP
public function register_settings() {
// build each settings section
foreach ( $this->sections as $section ) {
$section->build( $this->slug );
}
}
// Renders the options page
public function render() {
echo '<h1>' . esc_html( $this->title ) . '</h1> <br>';
// render the options form
echo '<form method="post" action="options.php">';
settings_fields( $this->slug );
do_settings_sections( $this->slug );
submit_button();
echo '</form>';
}
// $args acessor
function __get( $name ) {
return isset( $this->args[ $name ] ) ? $this->args[ $name ] : '';
}
}
/**
* Helps build a WordPress settings section with a much nicer interface. Yeah
* WordPress's Settings API sucks!
*
* Can be used like so:
*
* $settings = new SettingsBuilder();
*
* $settings->page( array(
* 'title' => 'My Page Title',
* 'menu' => 'Test Settings'
* ) );
*
* $settings->section( array(
* 'title' => 'Test Settings'
* ) );
*
* $settings->text( array(
* 'label' => __( 'Textual option', 'text_domain' ),
* 'option' => 'text_op'
* ) );
*
* $settings->bool( array(
* 'label' => __( 'Boolean option', 'text_domain' ),
* 'option' => 'bool_op'
* ) );
*
* $settings->select( array(
* 'label' => __( 'Select options', 'text_domain' ),
* 'option' => 'select_op',
* 'vals' => array(
* 'test1' => 'Test 1',
* 'test2' => 'Test 2',
* 'test3' => 'Test 3'
* )
* ) );
*
* // etc ...
*
* $settings->build();
*
* @author Zachary Scott <zscott.dev@gmail.com>
*/
class SettingsBuilder {
// list of settings pages
private $pages;
/**
* Creates a settings page on which subsequent sections will be placed.
*
* @param string $title The title of the settings page.
* @param string $menu The menu title
* @param string $cap The required capability for page to be visible
* (default 'manage_options').
*/
public function page( $args ) {
assert( ! empty( $args ) );
// add the page to the list
$this->pages[] = new Page( $args );
}
/**
* Creates a new section in which to place subsequent settings.
*
* @param string $title The title of the section to display to the user.
* @param string $page The theme page to which the section belongs.
*/
public function section( $args ) {
assert( ! empty( $args ) );
$end = sizeof( $this->pages );
$this->pages[ $end - 1 ]->section( $args );
}
/**
* Creates a boolean setting field (checkbox).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function bool( $args ) {
assert( ! empty( $args ) );
$end = sizeof( $this->pages );
$this->pages[ $end - 1 ]->bool( $args );
}
/**
* Creates a textual setting field (textbox).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function text( $args ) {
assert( ! empty( $args ) );
$end = sizeof( $this->pages );
$this->pages[ $end - 1 ]->text( $args );
}
/**
* Creates a multi-line textarea setting field (textarea).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function textarea( $args ) {
assert( ! empty( $args ) );
$end = sizeof( $this->pages );
$this->pages[ $end - 1 ]->textarea( $args );
}
/**
* Creates a number setting field (textbox).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $placeholder The placeholder text when no option is selected
* @param string $validate Validation callback
*/
public function number( $args ) {
assert( ! empty( $args ) );
$end = sizeof( $this->pages );
$this->pages[ $end - 1 ]->number( $args );
}
/**
* Creates a list of options (combobox).
*
* @param string $label The label to display on the page
* @param string $option The option name (wp option API)
* @param string $vals The list of available options
* @param string $validate Validation callback
*/
public function select( $args ) {
assert( ! empty( $args ) );
$end = sizeof( $this->pages );
$this->pages[ $end - 1 ]->select( $args );
}
/** Creates a <hr> */
public function hr() {
$end = sizeof( $this->pages );
$this->pages[ $end - 1 ]->hr( array() );
}
/** Registers the settings pages, sections and fields with wordpress. */
public function build() {
// build each settings page
foreach ( $this->pages as $page ) {
$page->build();
}
}
}
/** @file SettingsBuilder.php - Simple WP settings builder API
* Copyright (c) 2014, 2015 <zscott.dev@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment