WordPress как на ладони
WordPress Meetup #4. Встречаемся в Москве wordpress jino

API настроек (опций)

API опций было добавлено в версии 2.7 и позволяет создавать поля форм (опции), которые будут обрабатываться автоматически (сохраняться в БД и выводиться на экран). Такой подход дает возможность удобно добавлять свои опции и блоки опций в уже существующие страницы настроек (Общие, Чтение, Медиафайлы и т.д.). Или можно создавать страницы настроек плагинов без использования лишнего кода.

Несмотря на все удобства, регистрация и проверка значений полей никуда не исчезла - все это нужно делать вручную. Использование API позволяет избежать многих багов связанных с созданием страниц настроек плагинов в админке.

Заметка: все post данные формы должны отправляться на страницу wp-admin/options.php. Пользователи должны проходить проверку возможности: manage_options, а в мультисайтовых версиях должны быть Супер Админами (Super Admin), чтобы отправлять данные формы.

register_setting() и все функции регистрации опций и их блоков: add_settings_*(), должны вызываться в момент срабатывания хука admin_init

Функции API

Регистрация/удаление опций
Добавление блоков и отдельных полей
Вывод на экран
Ошибки

Добавление полей опций

Новое поле опции можно добавить в секцию на уже существующую страницу опций WordPress или на страницу опций плагина. Для этого используется функция add_settings_field().

Функция обратного вызова (callback) должна выводить html код поля input. Значение value поля также должно заполнятся. WordPress сам позаботится о сохранении опции в базу данных (обычно сохраняется в таблицу wp_options, но можно и перенастроить).

Заметка: добавляемая опция должна быть сначала зарегистрирована функцией register_setting, иначе она не будет сохраняться и обновляться.

add_settings_field( $id, $title, $callback, $page, $section = 'default', $args = array() );
  • $id - Ярлык (slug) опции, используется как идентификатор поля. Используется в ID атрибуте тега.

  • $title - заголовок поля.

  • $callback - название функции обратного вызова. Функция должна заполнять поле нужным <input> тегом, который станет частью одной большой формы. Атрибут name должен быть равен параметру $option_name из register_setting(). Атрибут id обычно равен параметру $id. Результат должен сразу выводиться на экран (echo).

  • $page - страница меню в которую будет добавлено поле. Указывать нужно sug страницы, т.е. параметр должен быть равен параметру $menu_slug из add_theme_page(). У базовых страниц WordPress названия равны: general, reading, writing и т.д. по аналогии...

  • $section - название секции страницы настроек, в которую должно быть добавлено поле. По умолчанию default или может быть секцией добавленной функцией add_settings_section.

  • $args - дополнительные параметры, которые должны быть переданы callback функции.

Добавление секции опций

Секции опций - это блоки опций выделенные заголовком. Вместо того, чтобы создавать новую страницу настроек, иногда разумнее добавить новую секцию на уже существующую страницу настроек WordPress - это сделает плагин более простым и WordPress не будет нагружен новой страницей настроек плагина.

add_settings_section( $id, $title, $callback, $page );
  • $id - идентификатор секции. К этому ID прикрепляются поля (см. add_settings_field()).

  • $title - заголовок секции (название блока).

  • $callback - функция обратного вызова, которая выполняется в начале секции, перед выводом полей. В ней можно вывести текст описывающий секцию, выводить нужно сразу на экран, используя echo.

  • $page -  тип страницы настроек, на которой будет показана секция (general, reading, writing, ...).

Регистрация опций

register_setting( $option_group, $option_name, $sanitize_callback );
unregister_setting( $option_group, $option_name, $sanitize_callback );
  • $option_group - название группы, к которой относится опция. Должна совпадать с группой в функции  settings_fields().

  • $option_name - название опции, которая будет сохраняться.

  • $sanitize_callback - функция обратного вызова, которая будет обрабатывать значение перед сохранением.

Вывод секции опций на экран

Когда API используется для добавления опций к уже существующей странице опций, нет необходимости беспокоится о html теге самой формы, потому что она уже добавлена на странице и опции (input поля) будут вставляться внутри этой формы. Однако, когда вы создаете новую страницу опций, то вам нужно указать HTML тег формы (form) и его структуру.

settings_fields

Чтобы вывести скрытые поля и обеспечить безопасность данных формы опций, используйте функцию settings_fields(): settings_fields( $option_group );

  • $option_group - название группы. Должно совпадать с таким же параметром у  register_setting(). Это название страницы (её slug) на которой выводятся опции.
do_settings_sections

Чтобы вывести секцию предназначенную для страницы опций, нужно использовать функцию do_settings_sections()do_settings_sections( $page );

  • $page - альтернативное название (slug) страницы секции которой нужно вывести. Должен совпадать с названием страницы в функции add_settings_section().

Функция do_settings_fields() похожа на do_settings_sections(), она также выводит поля для определенной страницы и секции, только эти поля не форматируются в табличный вид, а выводятся как есть. Обычно не нужно вызывать эту функцию напрямую, а нужно использовать do_settings_sections(), чтобы вывести поля опций связанные с секцией.

submit_button

Форма опций нуждается в кнопке отправки данных. Для этого, используйте функцию  submit_button().

Вывод всего блока формы

И в заключении вам нужно будет добавить HTML тег <form> в котором указать атрибуты action="options.php" method="POST". Этот пример, показывает как правильно выводить форму для отдельной страницы опций, с использованием ранее зарегистрированных полей:

<form method="POST" action="options.php">
<?php 
settings_fields( 'my-page' );  // название группы опций - register_setting( $option_group )
do_settings_sections( 'my-page' ); // slug страницы на которой выводится форма
submit_button();
?>
</form>

Примеры

#1. Дополнительные опции на странице настройки WordPress "Чтение"

Добавим новую секцию с двумя новыми опциями на базовую страницу настроек WordPress "Чтение"

// ------------------------------------------------------------------
// Вешаем все блоки, поля и опции на хук admin_init
// ------------------------------------------------------------------
//
add_action( 'admin_init', 'eg_settings_api_init' );
function eg_settings_api_init() {
	// Добавляем блок опций на базовую страницу "Чтение"
	add_settings_section(
		'eg_setting_section', // секция
		'Заголовок для секции настроек',
		'eg_setting_section_callback_function',
		'reading' // страница
	);

	// Добавляем поля опций. Указываем название, описание, 
	// функцию выводящую html код поля опции.
	add_settings_field(
		'eg_setting_name',
		'Описание поля опции',
		'eg_setting_callback_function', // можно указать ''
		'reading', // страница
		'eg_setting_section' // секция
	);
	add_settings_field(
		'eg_setting_name2',
		'Описание поля опции2',
		'eg_setting_callback_function2',
		'reading', // страница
		'eg_setting_section' // секция
	);

	// Регистрируем опции, чтобы они сохранялись при отправке 
	// $_POST параметров и чтобы callback функции опций выводили их значение.
	register_setting( 'reading', 'eg_setting_name' );
	register_setting( 'reading', 'eg_setting_name2' );
}

// ------------------------------------------------------------------
// Сallback функция для секции
// ------------------------------------------------------------------
//
// Функция срабатывает в начале секции, если не нужно выводить 
// никакой текст или делать что-то еще до того как выводить опции, 
// то функцию можно не использовать для этого укажите '' в третьем 
// параметре add_settings_section
//
function eg_setting_section_callback_function() {
	echo '<p>Текст описывающий блок настроек</p>';
}

// ------------------------------------------------------------------
// Callback функции выводящие HTML код опций
// ------------------------------------------------------------------
//
// Создаем checkbox и text input теги
//
function eg_setting_callback_function() {
	echo '<input 
		name="eg_setting_name" 
		type="checkbox" 
		' . checked( 1, get_option( 'eg_setting_name' ), false ) . ' 
		value="1" 
		class="code" 
	/>';
}
function eg_setting_callback_function2() {
	echo '<input 
		name="eg_setting_name2"  
		type="text" 
		value="' . get_option( 'eg_setting_name2' ) . '" 
		class="code2"
	 />';
}

Что получилось и какой блок за что отвечает:

Графическое изображение кода - что за что отвечает.

#2. Страница настроек плагина (опции в массиве)

Давайте создадим плагин, в котором будет только страница настроек. Сделаем мы это, чтобы разобраться как работает API настроек.

Здесь регистрируется одна опция и в нее добавляются несколько разных опций как массив данных. Так удобнее и практичнее.

<?php
/*
 * Plugin name: Primer
 * Description: Демонстрация создания страницы настроек для плагина
*/

/**
 * Создаем страницу настроек плагина
 */
add_action('admin_menu', 'add_plugin_page');
function add_plugin_page(){
	add_options_page( 'Настройки Primer', 'Primer', 'manage_options', 'primer_slug', 'primer_options_page_output' );
}

function primer_options_page_output(){
	?>
	<div class="wrap">
		<h2><?php echo get_admin_page_title() ?></h2>

		<form action="options.php" method="POST">
			<?php
				settings_fields( 'option_group' );     // скрытые защитные поля
				do_settings_sections( 'primer_page' ); // секции с настройками (опциями). У нас она всего одна 'section_id'
				submit_button();
			?>
		</form>
	</div>
	<?php
}

/**
 * Регистрируем настройки.
 * Настройки будут храниться в массиве, а не одна настройка = одна опция.
 */
add_action('admin_init', 'plugin_settings');
function plugin_settings(){
	// параметры: $option_group, $option_name, $sanitize_callback
	register_setting( 'option_group', 'option_name', 'sanitize_callback' );

	// параметры: $id, $title, $callback, $page
	add_settings_section( 'section_id', 'Основные настройки', '', 'primer_page' ); 

	// параметры: $id, $title, $callback, $page, $section, $args
	add_settings_field('primer_field1', 'Название опции', 'fill_primer_field1', 'primer_page', 'section_id' );
	add_settings_field('primer_field2', 'Другая опция', 'fill_primer_field2', 'primer_page', 'section_id' );
}

## Заполняем опцию 1
function fill_primer_field1(){
	$val = get_option('option_name');
	$val = $val ? $val['input'] : null;
	?>
	<input type="text" name="option_name[input]" value="<?php echo esc_attr( $val ) ?>" />
	<?php
}

## Заполняем опцию 2
function fill_primer_field2(){
	$val = get_option('option_name');
	$val = $val ? $val['checkbox'] : null;
	?>
	<label><input type="checkbox" name="option_name[checkbox]" value="1" <?php checked( 1, $val ) ?> /> отметить</label>
	<?php
}

## Очистка данных
function sanitize_callback( $options ){ 
	// очищаем
	foreach( $options as $name => & $val ){
		if( $name == 'input' )
			$val = strip_tags( $val );

		if( $name == 'checkbox' )
			$val = intval( $val );
	}

	//die(print_r( $options )); // Array ( [input] => aaaa [checkbox] => 1 )

	return $options;
}

В итоге получим такую страницу:

primer
Теперь давайте разберем код по порядку, от общего к частному...

Чтобы быстро усвоить принцип надо мыслить категориями, а не переменными...

Категория 1: Создание страницы настроек

Регистрируем страницу настроек другой функцией не относящийся к API настроек:

add_options_page( 'Настройки Primer', 'Primer', 'manage_options', 'primer_slug', 'primer_options_page_output' );
  • 'primer_slug' - это ярлык страницы настроек и он по сути в API настроек нигде не фигурирует...
Категория 2: Вывод секции на странице настроек

Страница настроек есть, теперь на ней нам нужно вызвать секцию с настройками. Самой секции пока нет и в ней тоже ничего нет. Секцию и её содержимое мы создадим чуть ниже:

        <form action="options.php" method="POST">
			<?php
			settings_fields( 'option_group' );     // скрытые защитные поля
			do_settings_sections( 'primer_page' ); // секции с настройками (опциями). У нас она всего одна 'section_id'
			submit_button();
			?>
		</form>
Категория 3: Регистрация/создание секции
add_settings_section( 'section_id', 'Основные настройки', '', 'primer_page' );
  • 'section_id' - чтобы к этой секции потом добавить поля
  • 'primer_page' - привязывает эту секцию к выводу секции do_settings_sections( 'primer_page' );
Категория 4: Заполнение секции опциями
    add_settings_field('primer_field1', 'Название опции', 'fill_primer_field1', 'primer_page', 'section_id' );
	add_settings_field('primer_field2', 'Другая опция', 'fill_primer_field2', 'primer_page', 'section_id' );
  • 'section_id' - добавляет поле в секцию.
  • 'primer_page' - дополнительное уточнение, что поле нужно добавить в секцию, которая вызывается с параметром do_settings_sections( 'primer_page' );.
Категория 5: Регистрация самой настройки

Сейчас у нас уже полностью готов весь вывод настроек, но сохранение не работает, потому что WordPress не знает о существовании новой опции. Нужно «сказать» WordPress, что есть настройка option_name и её нужно обрабатывать.

register_setting( 'option_group', 'option_name', 'sanitize_callback' );
  • 'option_group' - нужно для вывода скрытых полей, для защиты формы. Для settings_fields( 'option_group' );

  • 'option_name' - название самой опции, которая будет записываться в таблицу wp_options

  • 'sanitize_callback' - функция для изменения/очистки передаваемых данных. Её можно не указывать, тогда данные сохраняться как есть (как передаются).

#3. Поле с вводом телефона

Добавим поле с вводом телефона на страницу "Общие настройки":

add_action( 'admin_init', 'phone_settings_api_init' );

function phone_settings_api_init() {
	register_setting( 'general', 'phone', 'sanitize_text_field' );

	add_settings_field(
		'phone',
		'<label for="phone">Телефон</label>',
		'phone_field_html',
		'general'
	);
}

function phone_field_html() {
	$value = get_option( 'phone', '' );
	printf( '<input type="text" id="phone" name="phone" value="%s" />', esc_attr( $value ) );
}

Выведем сохраненный телефон на экран в нужном месте шаблона:

echo esc_html( get_option( 'phone', '' ) );
66 комментов
Полезные 7 Вопросы 5 Все
  • Сергей

    Подскажите, пожалуйста, как установить значения по умолчанию в полях страницы настроек плагина Primer.

  • Сережа

    Здравствуйте!

    Подскажите, как сделать так, чтобы при установке и активации плагина (взял за основу Primer) в поле fill_primer_field1 уже было задано значение по умолчанию?

    • campusboy2832 cайт: www.youtube.com/c/wpplus

      Привет. Вместо

      $val = $val ? $val['input'] : null;

      Заменить на

      $val = $val ? $val['input'] : 'Значение по умолчанию';
      1
      • Сережа

        Спасибо за ответ, получилось!

        Создал два поля input, как можно вывести одно из них в нужном месте?

        Первая опция называется get_option('option_name1'), вторая get_option('option_name2').

        Например так, не работает:

        add_action( 'wp_head', 'opt_customizer_css');
        function opt_customizer_css()
        {
        
        	?>
        		 <style type="text/css">
        			 body { background: <?php echo get_option('option_name1'); ?>; }
        		 </style>
        	<?php
        }

        Пытаюсь в настройках поля задать цвет фона, а затем его вывести в стили, как в кастомайзере, но не получается sad

        PS: значение по умолчанию тоже почему то для background не выводится, хотя в настройках оно сохранилось.

        При первой активации плагина в настройках значения есть, но их нет в таблице БД, поэтому background вообще пустой. После сохранения в настройках плагина опции сохраняются в БД, тогда background принимает значение Array.

        • campusboy2832 cайт: www.youtube.com/c/wpplus

          У вас видно туговато с основами PHP smile а пытаетесь кодить. Подтяните их и всё полетит сразу! Посмотрите код внимательно - всё сохраняется ведь в одну опцию. Опция в нашем случае - это массив с набором значений. Одно поле - ячейка массива с его значением. В данном коде это реализовано так, чтобы не плодить кучу строк с настройками, а хранить их в одном месте. Это избавляет нас делать множество запросов к БД для их получения. Один запрос - и все наши настройки на руках, причем закешированные. Запрос этих настроек ещё раз не приведет к запросу к БД - они возьмутся из кеша. Повторюсь, возвращаемые данные - массив. Потому и работать с ними надо как с массивом, как-то вот так:

          add_action( 'wp_head', 'opt_customizer_css' );
          function opt_customizer_css() {
          	// Получаем нашу опцию, где хранятся ВСЕ НАСТРОЙКИ (массив)
          	$options = get_option( 'option_name' );
          	// Проверяем, если сохраненные значения у поля с именем input и оно не пустое.
          	// Если пустое - подставляем значение по умолчанию.
          	$css = empty( $options['input'] ) ? '#fff' : $options['input'];
          	// Затем значение вставляем в CSS.
          	// Вместо очищающей функции esc_attr() можно что-нибудь более подходящее использовать.
          	?>
          	<style type="text/css">
          		body {
          			background: <?php echo esc_attr($css); ?>;
          		}
          	</style>
          	<?php
          }
          3
  • @ WahaWaher

    Где найти колбэк по успешной отправки опций в БД? Подскажите, плз.

    Ответить4 месяца назад #
  • Дмитрий cайт: d3j.ru
    $val = $val ? $val['input'] : null;

    Можно пояснить вот эту строчку?

    Ответить2 месяца назад #
    • lincaseidhe69 cайт: lincaseidhe.ru

      Это тернарный оператор, загугли как работает.

      1
      Ответить2 месяца назад #
    • Kama5401

      $val = если $val есть (не пустой), то $val['input'], в противном случае null...

      Ответить2 месяца назад #
      • Сергей

        Лучше проверять на наличия ключа в массиве, потому что, массив и так не пустой в нем уже есть текстовое поле, а вот чекбокс он удаляется с массива когда не cheked
        и $val['checkbox'] уже не актуальна. Notice: Undefined index: checkbox in
        if ( array_key_exists( 'checkbox', $val ) )
        $val = $val ? $val['checkbox'] : null;

        Ответить4 дня назад #
        • Сергей

          Или вот так сделать - $val = isset( $val['checkbox'] ) ? $val['checkbox'] : null;

          Ответить4 дня назад #
  • Дмитрий cайт: d3j.ru

    Как сделать, что бы параметр добавлялся в базу, а не перезаписывался?

    Ответить2 месяца назад #
Здравствуйте, !     Войти . Зарегистрироваться