3 подхода к добавлению настраиваемых полей в ваш плагин WordPress
Опубликовано: 2022-03-10Любой, кто создал плагин WordPress, понимает необходимость создания настраиваемых полей для изменения работы плагина. Существует бесчисленное множество вариантов использования настраиваемых параметров в плагине и почти столько же способов реализации указанных параметров. Видите ли, WordPress позволяет авторам плагинов создавать собственную разметку на своих страницах настроек. Как побочный эффект, страницы настроек могут сильно различаться между плагинами.
В этой статье мы рассмотрим три распространенных способа настройки вашего плагина. Мы начнем с создания страницы настроек и создадим наши поля, используя API настроек WordPress по умолчанию.
Дальнейшее чтение на SmashingMag:
- Расширьте возможности WordPress с помощью настраиваемых полей
- Хаки пользовательских полей для WordPress
- Расширение расширенных настраиваемых полей с помощью собственных элементов управления
- Полное руководство по пользовательским типам записей
Затем я расскажу вам, как настроить ваши поля с помощью пользовательского обработчика. Наконец, я покажу вам, как интегрировать отличный плагин настраиваемых полей Advanced Custom Fields (ACF) в ваш собственный плагин.
Поскольку это длинный пост, вот оглавление со ссылками на каждый из основных разделов:
- Создание нашего плагина и страницы настроек
- Подход 1: Использование встроенной функциональности WordPress
- Подход 2: Настройка пользовательской формы и обработчика
- Подход 3: интеграция ACF (расширенных настраиваемых полей) в ваш плагин
Примеры кода см. в репозитории, который я создал для сопровождения этого поста.
Создание нашего плагина и страницы настроек
Первое, что нам нужно сделать, это настроить наш плагин и создать страницу настроек. Все три подхода, описанные в этой статье, начинаются со структуры подключаемого модуля ниже. Структура этого плагина является объектно-ориентированной , поэтому в вашем собственном коде могут быть некоторые отличия, если ваш плагин написан процедурно. Обратите особое внимание на формат функции обратного вызова в действиях и фильтрах.
/* Plugin Name: Smashing Fields Plugin description: >- Setting up configurable fields for our plugin. Author: Matthew Ray Version: 1.0.0 */ class Smashing_Fields_Plugin { // Our code will go here } new Smashing_Fields_Plugin();
Внутри нашего класса мы собираемся добавить хук действия для добавления страницы настроек:
public function __construct() { // Hook into the admin menu add_action( 'admin_menu', array( $this, 'create_plugin_settings_page' ) ); }
Вы можете видеть, что обратный вызов нашего действия — create_plugin_settings_page
, поэтому давайте создадим этот метод. Примечание. Я установил аргументы как отдельные именованные переменные, чтобы придать контекст нашему коду, но вы можете просто поместить значения непосредственно в функцию для экономии памяти.
public function create_plugin_settings_page() { // Add the menu item and page $page_title = 'My Awesome Settings Page'; $menu_title = 'Awesome Plugin'; $capability = 'manage_options'; $slug = 'smashing_fields'; $callback = array( $this, 'plugin_settings_page_content' ); $icon = 'dashicons-admin-plugins'; $position = 100; add_menu_page( $page_title, $menu_title, $capability, $slug, $callback, $icon, $position ); }
Взгляните на кодекс WP для add_menu_page
для получения дополнительной информации.
Эта функция создаст нашу страницу, а также пункт меню. Важными частями здесь являются slug, возможность и аргументы обратного вызова. Слаг, который мы будем использовать позже для регистрации наших полей, так что запишите его где-нибудь. Вы можете изменить возможность, чтобы разрешить пользователям разного уровня доступ к вашей странице настроек. Что касается обратного вызова, мы вскоре создадим этот метод. Обратите внимание, что вы также можете поместить класс dashicon непосредственно в функцию, чтобы изменить значок для меню. Последний аргумент — это позиция пункта меню в меню; поэкспериментируйте с этим числом, чтобы найти место в меню, в котором вы хотите, чтобы ваши настройки упали. Примечание: вы можете использовать десятичные значения, чтобы избежать конфликтов с другими пунктами меню.
Наш следующий шаг — создать метод обратного вызова plugin_settings_page_content
для нашей страницы настроек.
public function plugin_settings_page_content() { echo 'Hello World!'; }
Если вы сохраните свой плагин и обновите панель администратора WordPress, вы должны увидеть следующее:

Вы можете видеть, что ваша страница настроек является пунктом меню верхнего уровня. Вы можете оставить это так, исходя из потребностей вашей страницы настроек. Однако вы также можете захотеть, чтобы настройки вашего плагина находились в другом пункте меню. В этом случае вы просто измените последнюю строку метода create_plugin_settings_page
на следующую:
add_submenu_page( 'options-general.php', $page_title, $menu_title, $capability, $slug, $callback );
Здесь мы меняем функцию add_menu_page
на add_submenu_page
и добавляем новый аргумент. Этот аргумент является родительским элементом меню, под которым будет находиться новая страница настроек. В документации add_submenu_page
вы можете увидеть довольно хороший список пунктов родительского меню и их слагов. Вы также можете заметить, что у нас больше нет двух последних аргументов, $icon
и $position
. Поскольку мы сейчас находимся в разделе подменю, мы больше не можем контролировать положение элемента. Кроме того, в подменю нет доступных значков, поэтому в этом аргументе нет необходимости.
Если вы сохраните этот новый код, вы увидите, что наша страница настроек будет отображаться в пункте меню «Настройки»:

Во многих случаях наиболее подходящим местом для добавления страницы настроек плагина является пункт «Настройки». В кодексе WordPress объясняется, что раздел «Настройки» используется для «отображения параметров плагина, которые должны просматривать только администраторы». Однако это всего лишь рекомендация, а не правило.
Теперь, когда у нас настроена страница настроек и мы знаем, как перемещать элементы, мы можем начать работать с полями. Работа, которую мы проделали до сих пор, будет повторно использована для различных методов, описанных ниже.
Подход 1: Использование встроенной функциональности WordPress
Прежде чем мы углубимся в код, давайте рассмотрим некоторые плюсы и минусы использования этого подхода.
Плюсы
- Легко интегрируется в существующие страницы настроек
- Санитарная обработка сделана для вас
- Вряд ли сломается, так как код управляется WordPress
- Может использоваться как для тем, так и для плагинов
- Гибкий, безопасный и расширяемый
Минусы
- Пользовательская проверка данных выполняется вручную
- Расширенные типы полей (ретрансляторы, карты, загрузки и т. д.) сложнее реализовать.
Когда следует использовать этот подход?
Этот подход достаточно гибок, чтобы его можно было настроить как для очень простых, так и для очень сложных страниц настроек. Вы можете использовать этот метод в большинстве ситуаций, если не возражаете делать некоторые вещи вручную.
Начиная
Используя этот подход, нам нужно будет следовать той же разметке, которую используют собственные страницы параметров WordPress. Мы должны изменить наш метод plugin_settings_page_content
следующим образом:
public function plugin_settings_page_content() { ?> <div class="wrap"> <h2>My Awesome Settings Page</h2> <form method="post" action="options.php"> <?php settings_fields( 'smashing_fields' ); do_settings_sections( 'smashing_fields' ); submit_button(); ?> </form> </div> <?php }
Приведенная выше разметка взята непосредственно из кодекса WordPress при создании страниц параметров. Имя метода должно совпадать с именем обратного вызова, которое мы указали в функции add_menu_page
выше. div
-оболочка на самом деле такой же, как форма WordPress по умолчанию, и будет извлекать стили из этих разделов. Тег form
указывает на обработчик формы параметров по умолчанию для WordPress.
Три строки PHP делают несколько вещей:
- Функция
settings_fields
в основном является ссылкой для остальных наших полей. Строковый аргумент, который вы помещаете в эту функцию, должен соответствовать переменной$slug
, которую мы настроили ранее — она будет во всех полях, которые мы зарегистрируем позже в плагине. Эта функция также выводит несколько скрытых входных данных для одноразового номера, действия формы и нескольких других полей для страницы параметров. - Следующая функция,
do_settings_sections
, является заполнителем для разделов и полей, которые мы зарегистрируем в другом месте нашего плагина. - Последняя функция,
submit_button
, будет выводить входные данные отправки, но также добавит некоторые классы в зависимости от статуса страницы. Могут быть и другие аргументы, которые вы захотите передать в функциюsubmit_button
; они изложены в кодексе.
Если мы обновим нашу страницу настроек, мы должны получить что-то вроде этого:

Он выглядит немного разреженным! Теперь приступим к настройке полей.
Разделы и поля
WordPress разделяет страницы параметров на разделы. Каждый раздел может иметь список связанных с ним полей. Нам нужно зарегистрировать раздел в нашем плагине, прежде чем мы сможем начать добавлять наши поля. Добавьте следующий код в функцию-конструктор:
add_action( 'admin_init', array( $this, 'setup_sections' ) );
Этот хук настроит разделы для нашей страницы. Вот код обратного вызова:
public function setup_sections() { add_settings_section( 'our_first_section', 'My First Section Title', false, 'smashing_fields' ); }
Первый аргумент — это уникальный идентификатор раздела, и мы будем использовать его для полей, которые хотим назначить разделу. Эти идентификаторы должны быть уникальными для всех новых разделов на этой странице. Следующий аргумент — это заголовок, который генерируется над разделом — вы можете сделать его каким угодно. Третий аргумент — обратный вызов. Сейчас у меня установлено значение false
, но мы вернемся к этому в ближайшее время. Четвертый аргумент — это страница параметров, на которую будут добавлены параметры (переменная $slug
ранее).
Так почему же в нашем обратном вызове есть false
? Ну, что-то, что не очень ясно при настройке параметров WordPress с использованием их документации, заключается в том, что несколько разделов могут совместно использовать обратный вызов. Часто, когда вы настраиваете обратный вызов, между хуком и обратным вызовом существует отношение 1 к 1. Итак, просто для примера попробуем создать три секции с одним и тем же обратным вызовом:
public function setup_sections() { add_settings_section( 'our_first_section', 'My First Section Title', array( $this, 'section_callback' ), 'smashing_fields' ); add_settings_section( 'our_second_section', 'My Second Section Title', array( $this, 'section_callback' ), 'smashing_fields' ); add_settings_section( 'our_third_section', 'My Third Section Title', array( $this, 'section_callback' ), 'smashing_fields' ); }
Все три из этих разделов имеют обратный вызов section_callback
, установленный в этом третьем слоте аргумента. Если затем мы создадим метод, соответствующий этому обратному вызову, и добавим туда «Hello World»:
public function section_callback( $arguments ) { echo '
Привет мир
'; }
мы получаем что-то вроде этого:

Я знаю, о чем вы думаете: «Какого черта мне нужен один и тот же текст во всех моих разделах?» Ответ заключается в том, что вы, вероятно, не стали бы. Здесь мы можем немного повозмущаться с функцией add_settings_section
. Если вы посмотрите на документацию для этой функции, вы увидите, что в разделе « Примечания » на странице функции обратного вызова будет назначен массив аргументов, которые напрямую соотносятся с аргументами в нашем хуке. Если вы войдете и var_dump( $arguments )
, вы увидите, что все аргументы передаются в нашу функцию.
Затем мы можем написать простой переключатель в нашем обратном вызове, чтобы изменить текст на основе переданного в него идентификатора:
public function section_callback( $arguments ) { switch( $arguments['id'] ){ case 'our_first_section': echo 'This is the first description here!'; break; case 'our_second_section': echo 'This one is number two'; break; case 'our_third_section': echo 'Third time is the charm!'; break; } }
Теперь у нас есть собственный текст для каждого раздела, который мы можем изменить в одной функции!

Конечно, для этих разделов также можно указать уникальные обратные вызовы, но такой подход позволяет объединить код в единую функцию. Эта идея работает так же для настройки полей. Мы можем сделать так, чтобы все наши поля совместно использовали обратный вызов, и он выдавал правильный тип поля на основе аргументов, которые мы передаем. Давайте добавим поля в наш метод конструктора. Поместите этот код сразу после перехвата секций в конструкторе:
add_action( 'admin_init', array( $this, 'setup_fields' ) );
Поскольку вы уже знаете упражнение, я просто дам вам обратный вызов для нашего действия:
public function setup_fields() { add_settings_field( 'our_first_field', 'Field Name', array( $this, 'field_callback' ), 'smashing_fields', 'our_first_section' ); }
Аргументы этой функции аналогичны аргументам функции section. Первый аргумент — это уникальный идентификатор поля. Второй — это метка, которая отображается рядом с полем. В третьем аргументе вы можете видеть, что я вызываю метод field_callback
; мы создадим этот обратный вызов всего за секунду. Четвертая — это страница параметров, которую мы хотим использовать (наша $slug
из предыдущего). Пятый аргумент — это уникальный идентификатор раздела , которому мы хотим присвоить это поле.
Вот код обратного вызова в нашем третьем аргументе:
public function field_callback( $arguments ) { echo '<input name="our_first_field" type="text" value="' . get_option( 'our_first_field' ) . '" />'; }
Здесь я просто копирую уникальный идентификатор поля в имя, идентификатор и нашу функцию get_option
. Давайте посмотрим, как выглядит наша страница с присоединенным новым полем:

Круто, у нас есть поле на странице! Попробуйте добавить в него какой-нибудь контент и нажмите «Сохранить изменения», я подожду здесь…
Вы делали это? Если вы все сделали правильно до этого момента, вы должны были получить сообщение об ошибке, говорящее что-то вроде ERROR: options page not found
или подобное. Причина, по которой это происходит, на самом деле является функцией безопасности в WordPress.
Видите ли, без этой функции пользователь мог бы войти в HTML и изменить имя поля на любое, которое он хотел, нажать «Сохранить», и эта опция была бы введена в базу данных с любым именем, которое она дала (при условии, что это было допустимое имя). название варианта). Это может позволить любому пользователю изменить параметры на других страницах (даже на тех, к которым он обычно не может получить доступ), просто введя правильное имя в поле и нажав «Сохранить» — не круто.
Эта проблема решается добавлением функции register_setting
. Если вы специально не скажете WordPress: «Эй, это поле можно сохранить на этой странице», WordPress не будет обновлять поле в базе данных. Итак, под нашей разметкой поля мы добавим эту новую функцию. Вот как выглядит обратный вызов после добавления кода:
public function field_callback( $arguments ) { echo '<input name="our_first_field" type="text" value="' . get_option( 'our_first_field' ) . '" />'; register_setting( 'smashing_fields', 'our_first_field' ); }
Первый аргумент в новой функции — это страница параметров, на которой мы хотим сохранить поле ( $slug
из предыдущего), а второй аргумент — это поле, которое мы хотим сохранить. Теперь попробуйте обновить поле — сработало!
Поздравляем! Вы только что сохранили свое первое поле с помощью API настроек WordPress. А что, если мы хотим иметь несколько разных типов полей, а не только текст? Давайте вернемся к нашему обратному вызову поля и поговорим о том, что переменная $arguments
передается в нашу функцию.
Аргументы поля
Если мы перейдем к обратному вызову нашего поля и var_dump( $arguments )
, мы получим пустой массив. Что дает? В нашем обратном вызове раздела мы получили кучу информации о разделе. Ну, здесь происходит что-то другое. Если вы посмотрите документацию для add_settings_field
, там есть пятый аргумент, который можно передать в функцию. Эта переменная напрямую связана с переменной $arguments
в нашем обратном вызове. Так что мы собираемся разместить наши новые вещи там.
Если мы посмотрим на одно из полей по умолчанию на странице настроек WordPress, мы увидим, что есть несколько областей, которые мы можем добавить к нашему полю, чтобы получить форматирование по умолчанию. Вот скриншот поля часового пояса на странице общих настроек:

Используя это поле в качестве отправной точки, давайте рассмотрим данные, которые мы хотим передать в наш обратный вызов поля.
- Уникальный идентификатор
- Метка поля (Часовой пояс в примере)
- В какой раздел он должен попасть
- Тип поля (текст, текстовое поле, выбор и т. д.)
- В случае, если есть несколько вариантов, нам нужны те
- Возможно, заполнитель, если тип поля поддерживает его.
- Вспомогательный текст (справа от поля в примере)
- Дополнительный текст (под полем в примере)
- Может быть, выбор по умолчанию, если он есть
Из этого списка мы можем настроить ассоциативный массив полей и значений, которые мы можем передать в наш обратный вызов:
public function setup_fields() { $fields = array( array( 'uid' => 'our_first_field', 'label' => 'Awesome Date', 'section' => 'our_first_section', 'type' => 'text', 'options' => false, 'placeholder' => 'DD/MM/YYYY', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => '01/01/2015' ) ); foreach( $fields as $field ){ add_settings_field( $field['uid'], $field['label'], array( $this, 'field_callback' ), 'smashing_fields', $field['section'], $field ); register_setting( 'smashing_fields', $field['uid'] ); } }
Итак, первое, что у нас есть, — это переменная с именем $fields
, которая будет содержать все поля, которые мы хотим создать. Внутри этого массива у нас есть другой массив, который содержит конкретные данные для каждого поля. Я настроил данные так, чтобы они точно соответствовали нашему списку выше. Затем я перебираю каждое поле (мы добавим больше) в массиве, добавляю поле и регистрирую его. В конце функции add_settings_field
я также добавляю весь массив данных для этого конкретного поля, чтобы мы могли кое-что сделать в функции обратного вызова. Давайте посмотрим на эту функцию обратного вызова здесь:
public function field_callback( $arguments ) { $value = get_option( $arguments['uid'] ); // Get the current value, if there is one if( ! $value ) { // If no value exists $value = $arguments['default']; // Set to our default } // Check which type of field we want switch( $arguments['type'] ){ case 'text': // If it is a text field printf( '<input name="%1$s" type="%2$s" placeholder="%3$s" value="%4$s" />', $arguments['uid'], $arguments['type'], $arguments['placeholder'], $value ); break; } // If there is help text if( $helper = $arguments['helper'] ){ printf( '<span class="helper"> %s</span>', $helper ); // Show it } // If there is supplemental text if( $supplimental = $arguments['supplemental'] ){ printf( '<p class="description">%s</p>', $supplimental ); // Show it } }
В приведенном выше примере мы делаем несколько вещей. Мы устанавливаем значения по умолчанию для полей, если они пусты, и добавляем вспомогательный и дополнительный текст. Однако самой важной частью нашего кода является оператор switch. В этом утверждении мы собираемся описать, как будут обрабатываться наши аргументы в зависимости от типа поля, который мы хотим.
Например, если у нас есть текстовое поле, нам не нужно иметь несколько вариантов. Однако раскрывающийся список <select>
должен иметь параметры для правильной работы. Поскольку у нас уже настроены текстовые типы, давайте запустим этот код и посмотрим, что мы получим.

Когда вы загружаете страницу настроек вашего плагина, вы должны увидеть верхнее поле на этом изображении. Нижняя часть — это то, что вы увидите, если удалите содержимое из поля (т. е. заполнитель). Если бы я удалил helper
или supplimental
аргументы из нашего массива полей, они должны были бы исчезнуть на странице настроек. Мы также можем изменить аргумент section
и изменить размещение поля в разделах.

Итак, у нас есть текстовые поля; как насчет более сложных типов полей? Давайте еще раз взглянем на наш оператор switch и добавим опцию для текстовых областей и одиночного выбора:
switch( $arguments['type'] ){ case 'text': // If it is a text field printf( '<input name="%1$s" type="%2$s" placeholder="%3$s" value="%4$s" />', $arguments['uid'], $arguments['type'], $arguments['placeholder'], $value ); break; case 'textarea': // If it is a textarea printf( '<textarea name="%1$s" placeholder="%2$s" rows="5" cols="50">%3$s</textarea>', $arguments['uid'], $arguments['placeholder'], $value ); break; case 'select': // If it is a select dropdown if( ! empty ( $arguments['options'] ) && is_array( $arguments['options'] ) ){ $options_markup = '; foreach( $arguments['options'] as $key => $label ){ $options_markup .= sprintf( '<option value="%s" %s>%s</option>', $key, selected( $value, $key, false ), $label ); } printf( '<select name="%1$s">%2$s</select>', $arguments['uid'], $options_markup ); } break; }
В приведенном выше коде вы заметите несколько различий между каждым из типов полей. Хотя текстовые области функционально аналогичны обычным текстовым полям, они требуют другой разметки. Выпадающие списки выбора — это совсем другое животное из-за опций. Нам нужно перебрать параметры и установить значения, выбранные состояния и метки. Так что наша разметка кардинально отличается.
Теперь, когда мы обновили нашу функцию обратного вызова, давайте посмотрим, как изменились данные поля:
$fields = array( array( 'uid' => 'our_first_field', 'label' => 'Awesome Date', 'section' => 'our_first_section', 'type' => 'text', 'options' => false, 'placeholder' => 'DD/MM/YYYY', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => '01/01/2015' ), array( 'uid' => 'our_second_field', 'label' => 'Awesome Date', 'section' => 'our_first_section', 'type' => 'textarea', 'options' => false, 'placeholder' => 'DD/MM/YYYY', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => '01/01/2015' ), array( 'uid' => 'our_third_field', 'label' => 'Awesome Select', 'section' => 'our_first_section', 'type' => 'select', 'options' => array( 'yes' => 'Yeppers', 'no' => 'No way dude!', 'maybe' => 'Meh, whatever.' ), 'placeholder' => 'Text goes here', 'helper' => 'Does this help?', 'supplemental' => 'I am underneath!', 'default' => 'maybe' ) );
Вот три поля, каждое из которых использует свой тип поля в нашем плагине. Первую мы уже прошли. Второе — это наше новое поле textarea
. Мы можем передать те же параметры с массивом (за исключением UID), но просто изменим наш тип, и вместо этого мы получим textarea
. Последнее поле в этом массиве — раскрывающийся список выбора. Основным обновлением здесь является добавление массива параметров. Мы добавляем простой ассоциативный массив с ключом массива в качестве значения параметра HTML и меткой. Используя этот массив, вот как выглядят наши поля:

Почти готово!
Теперь у нас есть работающая страница настроек плагина. Мы настроили разделы для страницы, опции, все обратные вызовы и зарегистрировали поля. Осталось только получить наши значения настроек в другом месте. Верьте или нет, мы уже сделали это. В верхней части нашего обратного вызова поля вы можете видеть, что мы проверяем значение базы данных:
$value = get_option( $arguments['uid'] );
Мы можем использовать тот же код в нашем плагине (или теме) и просто передать uid
в функцию. Поэтому, если бы я хотел получить значение поля our_first_field
, я бы просто написал:
get_option('our_first_field')
Вуаля! У нас есть наш замечательный плагин и наши потрясающие настройки! Очевидно, мы настроили только несколько типов полей, но я прошел и добавил больше в репозиторий кода для этого подхода (в частности, текстовые поля, пароли, числа, текстовые поля, раскрывающиеся списки выбора, множественный выбор, переключатели и флажки).
Подход 2: Настройка пользовательской формы и обработчика
Раньше этот подход был единственным способом добавления страниц настроек. До выхода WordPress 2.7 авторам плагинов приходилось создавать собственные формы и обработчики. Очевидно, это привело к большому количеству ошибок и несоответствий между плагинами. Хотя этот подход несколько устарел, в некоторых случаях он все еще является жизнеспособным вариантом.
Плюсы
- Вы можете отправить форму в пользовательские и удаленные обработчики
- Вы можете обойти некоторые встроенные ограничения API настроек.
Минусы
- Совместимость должна поддерживаться разработчиком
- Необходимо дезинфицировать и проверять вручную
Когда следует использовать этот подход?
Используйте этот подход, когда вам абсолютно необходимо иметь настраиваемый обработчик или настраиваемый интерфейс. Вероятно, в большинстве случаев вы можете обойтись без Подхода 1, но у вас больше гибкости при проверке и обработке с использованием этого метода.
Начиная
Прежде чем мы углубимся в детали, нам нужно придумать сценарий, в котором мы будем использовать собственный обработчик. Для простоты давайте создадим форму, которая будет проверять имя пользователя и адрес электронной почты. Мы могли бы получить данные из базы данных или даже с удаленного сервера. В этом случае я создам массив с действительными именами пользователей и адресами электронной почты, чтобы мы могли их сверить. Затем мы сохраним значения полей, введенные пользователем, и сохраним их в базе данных.
Создание формы
Как я упоминал ранее, мы будем следовать тем же шагам, что и в разделе «Создание нашего плагина и страницы настроек». Для этого подхода мы настроим наши поля, используя статическую разметку HTML. Мы добавим этот код в обратный вызов из функции plugin_settings_page_content
следующим образом:
public function plugin_settings_page_content() { ?> <div class="wrap"> <h2>My Awesome Settings Page</h2> <form method="POST"> <table class="form-table"> <tbody> <tr> <th><label for="username">Username</label></th> <td><input name="username" type="text" value="" class="regular-text" /></td> </tr> <tr> <th><label for="email">Email Address</label></th> <td><input name="email" type="text" value="" class="regular-text" /></td> </tr> </tbody> </table> <p class="submit"> <input type="submit" name="submit" class="button button-primary" value="Check My Info!"> </p> </form> </div> <?php }
Вы можете видеть выше, что мы просто добавляем статический HTML для наших полей. Мы дублируем разметку со страниц основных настроек WordPress. Мы также опускаем действие формы, чтобы оно отправляло форму на текущую страницу, в отличие от обработчика options.php по умолчанию.
Прежде чем мы напишем наш собственный обработчик, давайте быстро добавим некоторые функции безопасности. Первое, что мы должны сделать, это поместить одноразовый номер в нашу форму. Nonces защитит от попыток подделки межсайтовых запросов, что похоже на спуфинг пользователя или атаки повторного воспроизведения. Давайте поместим одноразовый номер в наш HTML:
<form method="POST"> <?php wp_nonce_field( 'awesome_update', 'awesome_form' ); ?> <table class="form-table">
wp_nonce_field
добавит в нашу форму пару скрытых полей; одноразовый номер и реферер. Мы вернемся к одноразовому номеру, когда пройдемся по коду обработчика.
Далее нам нужно добавить поле, чтобы определить, когда форма была обновлена. Мы можем сделать это, просто добавив скрытое поле вверху нашей формы:
<form method="POST"> <input type="hidden" name="updated" value="true" /> <?php wp_nonce_field( 'awesome_update', 'awesome_form' ); ?> <table class="form-table">
Теперь мы можем поместить фрагмент кода в верхнюю часть нашей страницы, чтобы определить, когда наша форма отправлена, и отправить нас нашему пользовательскому обработчику:
public function plugin_settings_page_content() { if( $_POST['updated'] === 'true' ){ $this->handle_form(); } ?> <div class="wrap"> <h2>My Awesome Settings Page</h2>
Здесь мы просто проверяем, было ли отправлено скрытое updated
поле, и, если это так, мы вызываем метод в нашем плагине с именем handle_form
. Здесь мы можем начать писать наш собственный обработчик.
Создание обработчика
Есть несколько вещей, которые нам нужно проверить в обработчике, прежде чем мы на самом деле будем управлять данными формы. Сначала нам нужно проверить, существует ли наш одноразовый номер и является ли он действительным:
public function handle_form() { if( ! isset( $_POST['awesome_form'] ) || ! wp_verify_nonce( $_POST['awesome_form'], 'awesome_update' ) ){ ?> <div class="error"> <p>Sorry, your nonce was not correct. Please try again.</p> </div> <?php exit; } else { // Handle our form data } }
Приведенный выше код проверяет правильность одноразового номера. Если он недействителен, мы даем пользователю сообщение о том, почему форма не была обновлена. Если одноразовый номер существует и является правильным, мы можем обработать нашу форму. Теперь давайте напишем код для нашего обработчика формы (этот код заменит комментарий во фрагменте выше):
$valid_usernames = array( 'admin', 'matthew' ); $valid_emails = array( '[email protected]', '[email protected]' ); $username = sanitize_text_field( $_POST['username'] ); $email = sanitize_email( $_POST['email'] ); if( in_array( $username, $valid_usernames ) && in_array( $email, $valid_emails ) ){ update_option( 'awesome_username', $username ); update_option( 'awesome_email', $email );?> <div class="updated"> <p>Your fields were saved!</p> </div> <?php } else { ?> <div class="error"> <p>Your username or email were invalid.</p> </div> <?php }
Давайте рассмотрим код в этом разделе. Первые несколько строк — это массивы действительных имен пользователей/адресов электронной почты, которые мы будем проверять. Эти значения массива могут быть заполнены откуда угодно.
Следующие две строки — это значения, которые наш пользователь ввел в форму. Мы используем встроенные функции очистки, которые предоставляет нам WordPress. Этот шаг важен, чтобы избежать нескольких уязвимостей с веб-формами. Затем мы проверяем, входят ли значения, предоставленные пользователями, в наш массив допустимых значений. Если они есть, обновите параметры формы в базе данных. Этот шаг также можно заменить настраиваемым механизмом хранения. Мы также даем пользователю сообщение о том, что его поля были сохранены. Если ввод пользователя недействителен, мы сообщаем ему об этом.
Последнее, что нам нужно сделать, это показать сохраненные поля в форме после их ввода. Мы сделаем это так же, как извлекали бы эти поля в другом месте плагина: с помощью функции get_option
. Вот поля после того, как мы добавим правильный код:
<tr> <th><label for="username">Username</label></th> <td><input name="username" type="text" value="<?php echo get_option('awesome_username'); ?>" class="regular-text" /></td> </tr> <tr> <th><label for="email">Email Address</label></th> <td><input name="email" type="text" value="<?php echo get_option('awesome_email'); ?>" class="regular-text" /></td> </tr>
Теперь мы готовы проверить нашу форму. Попробуйте ввести имя пользователя admin и адрес электронной почты [email protected] . В форме вы должны получить следующее:

Если вы попытаетесь установить для какого-либо поля недопустимое значение, вы должны получить сообщение об ошибке, и поля не должны обновляться из-за нашего пользовательского обработчика.
Вот и все для нашего второго подхода! Теперь вы настроили пользовательскую форму и обработчик для управления полями вашего плагина. Готовый код для этого подхода можно найти в репозитории этой статьи. Теперь давайте перейдем к нашему последнему подходу.
Подход 3: интеграция ACF (расширенных настраиваемых полей) в ваш плагин
Если вы еще не использовали ACF Эллиота Кондона, позвольте представить вам. ACF — прекрасный полевой менеджер для WordPress. Одна из лучших вещей в нем — интерфейс конфигурации поля. Это позволяет довольно легко разворачивать поля для нескольких разных страниц в WordPress (таких как записи, страницы, пользователи, таксономии и даже их собственные интегрированные страницы параметров). Вы можете подумать: «Я не могу интегрировать чужой плагин в свой — это сомнительно!» но г-н Кондон понимает бедственное положение своих коллег-разработчиков и запланировал это в своем плагине. Вы можете просмотреть его документацию по этой теме, но я приведу некоторые из них здесь. Пройдемся по правилам.
- Во-первых, если вы распространяете бесплатный плагин, вы должны использовать бесплатную версию ACF. Это сделано для того, чтобы люди не могли получить PRO-версию из вашего бесплатного плагина — это было бы не круто. Вы можете без проблем использовать PRO-версию в премиальных плагинах и темах, просто убедитесь, что вы купили лицензию разработчика.
- Во-вторых, не изменяйте информацию об авторских правах ACF. Отдайте должное мужчине!
- Наконец, не распространяйте лицензионный ключ вместе с вашим плагином.
Теперь плюсы и минусы этого подхода:
Плюсы
- Очень легко интегрируется в темы и плагины
- Вы можете воспользоваться расширенными полями, которые являются частью ACF
- Код и обновления безопасности управляются командой ACF.
- ACF имеет отличные дополнения и поддержку, если вы застряли
- Настройка ваших полей очень проста благодаря пользовательскому интерфейсу конфигурации поля.
Минусы
- Ограниченный доступ к разметке
- Создает зависимость для вашего плагина или темы
- Чтобы поддерживать ACF в актуальном состоянии, вы должны обновлять его в файлах вашего плагина/темы (подробнее ниже).
Когда следует использовать этот подход?
Когда вы хотите очень быстро создать интерфейс с расширенными настройками, и вам не нужно настраивать внешний вид.
Начиная
For this approach I will show you how to set up the options page for the free version of ACF. To view a guide on setting up the PRO version check out the ACF documentation. To get started we will be adding ACF to our plugin directory. First, download the latest version of ACF and unzip its contents. In your plugin directory create a (Посмотреть большую версию)
Again, we will follow the steps we did in “Creating Our Plugin And Settings Page”. Before we get into that, though, we should include ACF in our plugin.
Include ACF In Your Plugin
It's actually pretty easy to include ACF in your plugin – there are only three steps. First we have to include the main ACF file with PHP. Add the following code to the bottom of our constructor function:
include_once( plugin_dir_path( __FILE__ ) . 'vendor/advanced-custom-fields/acf.php' );
If you refresh the admin you should see a new menu item titled Custom Fields . Before we go into that page and start setting up our fields we need to update a couple of paths in ACF. We need to tell ACF to look for its front-end assets and file includes in our plugin directory instead of its normal place. Add these two hooks to your constructor:
add_filter( 'acf/settings/path', array( $this, 'update_acf_settings_path' ) ); add_filter( 'acf/settings/dir', array( $this, 'update_acf_settings_dir' ) );
And the callbacks for those hooks:
public function update_acf_settings_path( $path ) { $path = plugin_dir_path( __FILE__ ) . 'vendor/advanced-custom-fields/'; return $path; } public function update_acf_settings_dir( $dir ) { $dir = plugin_dir_url( __FILE__ ) . 'vendor/advanced-custom-fields/'; return $dir; }
The first callback updates the include paths for the PHP files within the ACF plugin. The second updates the URIs for the ACF assets. Now we can set up our fields.
Configuring Your Fields
Now comes the fun part: the ACF field configuration UI. Add a title and any fields you'd like in your form. There is a great walkthrough of what everything on this page does in the ACF documentation. Here's how I have set up mine:

Once you are ready hit the Publish button on the right and your fields will be saved. Now we have to get the fields we set up into our plugin. Right now they only exist in the database. On the left-hand navigation, click the Tools item. On the new page, select the field group we just created and hit Generate Export Code . This will create a chunk of PHP code that we can now include in our plugin.
To add the options, we need to add a method call to our constructor. Add this line to the end of your constructor after our ACF include:
$this->setup_options();
Then we can create the method that will wrap our options:
public function setup_options() { if( function_exists( 'register_field_group' ) ) { register_field_group(array ( 'id' => 'acf_awesome-options', 'title' => 'Awesome Options', 'fields' => array ( array ( 'key' => 'field_562dc35316a0f', 'label' => 'Awesome Name', 'name' => 'awesome_name', 'type' => 'text', 'default_value' => ', 'placeholder' => ', 'prepend' => ', 'append' => ', 'formatting' => 'html', 'maxlength' => ', ), array ( 'key' => 'field_562dc9affedd6', 'label' => 'Awesome Date', 'name' => 'awesome_date', 'type' => 'date_picker', 'date_format' => 'yymmdd', 'display_format' => 'dd/mm/yy', 'first_day' => 1, ), array ( 'key' => 'field_562dc9bffedd7', 'label' => 'Awesome WYSIWYG', 'name' => 'awesome_wysiwyg', 'type' => 'wysiwyg', 'default_value' => ', 'toolbar' => 'full', 'media_upload' => 'yes', ), ), 'location' => array ( array ( array ( 'param' => 'options_page', 'operator' => '==', 'value' => 'smashing_fields', ), ), ), 'menu_order' => 0, 'position' => 'normal', 'style' => 'default', 'label_placement' => 'top', 'instruction_placement' => 'label', 'hide_on_screen' => ', 'active' => 1, 'description' => ', )); } }
Now that we have our fields ready to go, we can add them to the settings page.
Modifying The Settings Page Code
To add the fields we just created to the page we will need to update our plugin_settings_page_content
method.
Previously, we set up the form tag for our page. In this case we will let ACF do that part for us. Here is what our updated function should look like:
public function plugin_settings_page_content() { do_action('acf/input/admin_head'); // Add ACF admin head hooks do_action('acf/input/admin_enqueue_scripts'); // Add ACF scripts $options = array( 'id' => 'acf-form', 'post_id' => 'options', 'new_post' => false, 'field_groups' => array( 'acf_awesome-options' ), 'return' => admin_url('admin.php?page=smashing_fields'), 'submit_value' => 'Update', ); acf_form( $options ); }
Первые две строки нашей функции добавляют скрипты и стили, которые нам понадобятся для полей настроек. После этого мы настраиваем параметры нашей формы. Вы можете заметить, что значение в аргументе field_groups
соответствует идентификатору из нашей функции register_field_group
. Чтобы увидеть другие параметры конфигурации, посмотрите документацию acf_form
. Последняя строка в нашей функции фактически будет отображать форму.
Если вы попытаетесь загрузить страницу настроек сейчас, вы увидите, что страница на самом деле не работает. Это связано с тем, что ACF необходимо локализовать несколько переменных для JavaScript. Для этого нам нужно добавить еще один хук в наш конструктор:
add_action( 'admin_init', array( $this, 'add_acf_variables' ) );
Теперь нам нужно настроить обратный вызов:
public function add_acf_variables() { acf_form_head(); }
Вы можете попробовать добавить некоторый контент и сохранить, и это должно работать так же, как два других наших подхода. Вот как должна выглядеть наша страница:

Есть только пара пунктов очистки, которые нам нужно решить:
Возможно, вы захотите скрыть тот факт, что вы используете ACF для своего плагина. В этом случае вам нужно скрыть пункт меню « Пользовательские поля» . Вы можете сделать это, добавив этот фрагмент в функцию-конструктор:
add_filter( 'acf/settings/show_admin', '__return_false' );
Мы должны удалить версию базы данных нашей группы полей. Просто перейдите в раздел настраиваемых полей и нажмите кнопку корзины. Этот шаг является необязательным, поскольку он повлияет только на среду, в которой вы создали плагин, но может вызвать проблемы, если вы тестируете свой плагин в той же среде.
Использование полей ACF в вашем плагине
Чтобы получить созданные вами поля ACF, вам просто нужно использовать функцию get_field
ACF по умолчанию. Первый параметр должен быть уникальным идентификатором поля, а второй аргумент должен иметь значение 'option'
. Вот как мы получим поле Awesome Date :
get_field( 'awesome_date', 'option' )
Вот и все. Теперь вы настроили плагин с настройками ACF! Вы можете просмотреть код для этого подхода в репозитории.
Заключение
Итак, у вас есть три способа сделать ваши плагины настраиваемыми. Я хотел бы услышать о плагинах, которые вы можете создавать, используя эти методы. Я снова создал репозиторий, в котором хранится код для каждого из этих подходов. Пожалуйста, не стесняйтесь разветвлять их и настраивать в соответствии с вашими потребностями.
Что касается моих личных предпочтений в отношении этих подходов, я склоняюсь к подходу 1. Я предпочитаю оставлять как можно меньше зависимостей в своих плагинах, и вы можете настроить разметку в соответствии с темой страницы настроек для пользовательских проектов. Для быстрых прототипов или проектов, где мне нужно настроить очень сложные поля, я буду использовать ACF, но это усложняет управление обновлениями плагинов.
Также стоит упомянуть предложение Скотта Кларка относительно Fields API. Хотя в настоящее время он все еще находится в стадии разработки, API, по сути, позволит разработчикам плагинов и тем использовать тот же интерфейс, что и Customizer API, для создания полей настроек в других областях панели администратора.
Альтернативы АКФ
Как указано в комментариях ниже, и чтобы предоставить разработчикам другие варианты, похожие на подход ACF, вы можете проверить некоторые альтернативы, которые могут предлагать другие функции. Если у вас есть больше, пожалуйста, отправьте их в комментариях ниже! Здесь они в произвольном порядке:
- CMB2 от WebDevStudios
- Метабокс Тран Нгок Туан Ань (Рилвис)