Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Options and dashboard pages

The phone number in the header, a third-party service key, the number of products per page must not be written into the code: the site owner, not a programmer, will change them. WordPress stores such values in options and lets you add a dashboard page for them.

Reading and writing

update_option('mytheme_phone', '+1 202 555 0123');
 
$phone = get_option('mytheme_phone', '');
 
delete_option('mytheme_phone');

The second argument of get_option() is the value used when the option does not exist yet. The value may be an array: WordPress stores it by itself.

Printing in a template

<?php
// header.php
 
$phone = get_option('mytheme_phone');
 
if ($phone) {
    printf('<a href="tel:%s">%s</a>', esc_attr(preg_replace('/[^+\d]/', '', $phone)), esc_html($phone));
}

A page in the dashboard

// functions.php
 
add_action('admin_menu', function () {
    add_options_page(
        'Theme settings',
        'Theme',
        'manage_options',
        'mytheme',
        'mytheme_render_settings',
    );
});

The arguments: the page title, the menu label, the required capability, the page slug and the function that prints it. The item appears under "Settings".

FunctionWhere it adds the item
add_options_page()Under "Settings"
add_menu_page()In the main dashboard menu
add_submenu_page()In the submenu of any section
add_theme_page()Under "Appearance"

Registering options

The Settings API takes care of saving and of checking the token and the rights:

add_action('admin_init', function () {
    register_setting('mytheme', 'mytheme_phone', [
        'type' => 'string',
        'sanitize_callback' => 'sanitize_text_field',
        'default' => '',
    ]);
 
    add_settings_section('mytheme_contacts', 'Contacts', '__return_null', 'mytheme');
 
    add_settings_field('mytheme_phone', 'Phone', function () {
        printf(
            '<input type="text" name="mytheme_phone" value="%s" class="regular-text">',
            esc_attr(get_option('mytheme_phone', '')),
        );
    }, 'mytheme', 'mytheme_contacts');
});

sanitize_callback cleans the value before it is saved.

The form

function mytheme_render_settings(): void
{
    ?>
    <div class="wrap">
        <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
 
        <form method="post" action="options.php">
            <?php
            settings_fields('mytheme');
            do_settings_sections('mytheme');
            submit_button();
            ?>
        </form>
    </div>
    <?php
}

settings_fields() prints the hidden fields with the token, do_settings_sections() the sections and fields, submit_button() the "Save" button. WordPress handles the form itself.

Your own check

register_setting('mytheme', 'mytheme_per_page', [
    'type' => 'integer',
    'default' => 12,
    'sanitize_callback' => function ($value): int {
        $value = absint($value);
 
        return $value >= 1 && $value <= 100 ? $value : 12;
    },
]);

Autoloading

By default options are loaded on every page view — in one query, all at once. Large values that are rarely needed are saved without autoloading:

update_option('mytheme_import_log', $log, false);

Common mistake

A dashboard page without a capability check is open to any logged-in user who knows its address. The third argument of add_options_page() — manage_options — closes it to everyone but administrators. If your own code handles the form rather than the Settings API, check both the current_user_can('manage_options') capability and the token with check_admin_referer() in it.

Thanks for reading!

Donate to the site — we work hard for you.