Создание пользовательских страниц в панели управления WordPress
Создание страниц админки в WordPress с помощью PHP
Наиболее эффективный способ добавления собственных страниц в панель управления WordPress - использование функций add_menu_page и add_submenu_page. Эти функции регистрируют пункты меню и связывают их с функциями отображения контента. Пример базового подхода:
add_action('admin_menu', 'my_custom_admin_page');
function my_custom_admin_page() {
add_menu_page(
'Заголовок страницы',
'Название меню',
'manage_options',
'my-plugin-slug',
'my_admin_page_display',
'dashicons-admin-generic',
30
);
}
function my_admin_page_display() {
?>
<div class="wrap">
<h1>Моя страница админки</h1>
<p>Содержимое страницы.</p>
</div>
<?php
}Function php wp (функции wordpress php)
Параметры: заголовок страницы (в теге <title>), название пункта меню, необходимые права (capability), уникальный slug, функция обратного вызова, иконка (можно передавать URL или dashicon), позиция в меню. После этого страница становится доступна в боковом меню.
Типичные ошибки:
- Неправильный slug - приводит к тому, что страница не отображается или конфликтует с другими плагинами.
- Отсутствие хука
admin_menu- без него функции регистрации не сработают. - Использование неверной capability - пользователь без прав не увидит пункт меню.
- Забытая директива
?>в начале HTML - приводит к синтаксической ошибке.
Как добавить страницу настроек с использованием Settings API?
Создание страницы настроек, которая автоматически сохраняет данные в таблицу wp_options с помощью Settings API. Пример:
add_action('admin_menu', 'my_settings_page');
function my_settings_page() {
add_options_page(
'Настройки плагина',
'Мой плагин',
'manage_options',
'my-plugin-settings',
'my_settings_page_display'
);
}
add_action('admin_init', 'my_register_settings');
function my_register_settings() {
register_setting('my_settings_group', 'my_option_name');
add_settings_section(
'my_section',
'Основной раздел',
'my_section_callback',
'my-plugin-settings'
);
add_settings_field(
'my_field',
'Текстовое поле',
'my_field_callback',
'my-plugin-settings',
'my_section'
);
}Php woocommerce content (работа с контентом woocommerce в php)
Проблемы:
- Несовпадение slug страницы и slug секции - настройки не отображаются.
- Отсутствие вызова
settings_fields()иdo_settings_sections()в форме - данные не сохраняются. - Игнорирование nonce - уязвимость CSRF.
Как создать страницу с кастомной таблицей данных на основе WP_List_Table?
Для отображения списков записей (например, из произвольной таблицы БД) удобно наследовать класс WP_List_Table. Пример:
if (!class_exists('WP_List_Table')) {
require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
}
class My_Custom_Table extends WP_List_Table {
function get_columns() {
return ['cb' => '<input type="checkbox" />', 'name' => 'Имя', 'email' => 'Email'];
}
function prepare_items() {
$this->items = [
['name' => 'Иван', 'email' => 'ivan@example.com'],
['name' => 'Петр', 'email' => 'petr@example.com']
];
}
function column_default($item, $column_name) {
return $item[$column_name];
}
}
Admin pages php (страницы админки на php)
Вывод таблицы на странице админки:
$table = new My_Custom_Table();
$table->prepare_items();
$table->display();Wp themes php (темы wordpress на php)
Ошибки:
- Отсутствие обработки bulk-действий - таблица не реагирует на массовые операции.
- Неправильная установка колонок и сортировка - данные отображаются хаотично.
- Игнорирование пагинации - при большом количестве записей всё грузится медленно.
Как создать страницу админки с использованием ООП (класса)?
Объектно-ориентированный подход улучшает читаемость и повторное использование кода. Пример класса, который регистрирует страницу:
class My_Admin_Page {
public function __construct() {
add_action('admin_menu', [$this, 'add_admin_menu']);
}
public function add_admin_menu() {
add_menu_page(
'ООП страница',
'Мой класс',
'manage_options',
'my-oop-page',
[$this, 'render_page'],
'dashicons-admin-site',
25
);
}
public function render_page() {
echo '<div class="wrap"><h1>Привет из ООП</h1></div>';
}
}
new My_Admin_Page();Проблемы:
- Несовместимость с PHP 5.2 - использование коротких массивов и анонимных функций.
- Множественные экземпляры класса - повторная регистрация меню.
- Отсутствие автозагрузки - класс не найден до хука.
Как добавить страницу в существующее меню (например, Инструменты)?
Для добавления подменю к стандартным разделам используют add_submenu_page с указанием родительского slug. Пример для «Инструменты»:
add_action('admin_menu', 'my_tools_page');
function my_tools_page() {
add_submenu_page(
'tools.php',
'Мой инструмент',
'Мой инструмент',
'manage_options',
'my-tools',
'my_tools_page_callback'
);
}Ошибка: неверный родительский slug (например, 'tools' вместо 'tools.php') - пункт меню не появляется.
Как ограничить доступ к странице по ролям пользователей?
Используйте параметр capability. Для сложных проверок применяйте фильтр user_has_cap или кастомные capability. Пример:
add_action('admin_menu', 'my_role_based_page');
function my_role_based_page() {
if (current_user_can('editor') || current_user_can('administrator')) {
add_menu_page(
'Страница для редакторов',
'Редакторский раздел',
'edit_posts',
'editor-page',
'editor_page_callback'
);
}
}Проблема: проверка на current_user_can до вызова add_menu_page приводит к тому, что пункт меню может не появиться у пользователя, но страница останется доступной по прямому URL. Лучше использовать capability в самой функции регистрации.
Расширенные примеры создания страниц админки
Пример 1: Полноценный плагин с двумя страницами (настройки и список записей)
<?php
/*
Plugin Name: Demo Admin Pages
*/
class Demo_Admin_Pages {
public function __construct() {
add_action('admin_menu', [$this, 'register_pages']);
add_action('admin_init', [$this, 'register_settings']);
}
public function register_pages() {
// Основная страница
add_menu_page(
'Демо плагин',
'Демо',
'manage_options',
'demo-main',
[$this, 'render_main'],
'dashicons-admin-generic',
20
);
// Подстраница - настройки
add_submenu_page(
'demo-main',
'Настройки',
'Настройки',
'manage_options',
'demo-settings',
[$this, 'render_settings']
);
}
public function register_settings() {
register_setting('demo_settings_group', 'demo_text_option');
add_settings_section('demo_section', 'Основные настройки', null, 'demo-settings');
add_settings_field('demo_field', 'Текст', [$this, 'field_callback'], 'demo-settings', 'demo_section');
}
public function field_callback() {
$value = get_option('demo_text_option', '');
echo "<input type='text' name='demo_text_option' value='" . esc_attr($value) . "' />";
}
public function render_main() {
?>
<div class="wrap">
<h1>Главная страница демо</h1>
<p>Здесь можно вывести произвольную информацию.</p>
</div>
<?php
}
public function render_settings() {
?>
<div class="wrap">
<h1>Настройки демо</h1>
<form action="options.php" method="post">
<?php settings_fields('demo_settings_group'); ?>
<?php do_settings_sections('demo-settings'); ?>
<?php submit_button(); ?>
</form>
</div>
<?php
}
}
new Demo_Admin_Pages();Результат: в меню появятся пункты «Демо» и подпункт «Настройки». На странице настроек отображается текстовое поле, значение сохраняется в wp_options.
После активации плагина в боковом меню WordPress появляется новая секция.
Пример 2: Страница с таблицей данных из кастомной таблицы БД
global $wpdb;
$table_name = $wpdb->prefix . 'custom_data';
// Создание таблицы (выполняется однократно при активации)
function create_custom_table() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}custom_data (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
email varchar(100) NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
register_activation_hook(__FILE__, 'create_custom_table');
// Класс для отображения таблицы
if (!class_exists('WP_List_Table')) {
require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
}
class Custom_Data_Table extends WP_List_Table {
function get_columns() {
return ['cb' => '<input type="checkbox" />', 'name' => 'Имя', 'email' => 'Email'];
}
function prepare_items() {
global $wpdb;
$per_page = 10;
$current_page = $this->get_pagenum();
$total_items = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}custom_data");
$this->set_pagination_args([
'total_items' => $total_items,
'per_page' => $per_page
]);
$offset = ($current_page - 1) * $per_page;
$this->items = $wpdb->get_results(
"SELECT name, email FROM {$wpdb->prefix}custom_data LIMIT $per_page OFFSET $offset",
ARRAY_A
);
}
function column_default($item, $column_name) {
return esc_html($item[$column_name]);
}
function get_bulk_actions() {
return ['delete' => 'Удалить'];
}
}
// Страница админки
add_action('admin_menu', function() {
add_menu_page('Данные', 'Данные', 'manage_options', 'custom-data', 'render_custom_data_page');
});
function render_custom_data_page() {
$table = new Custom_Data_Table();
$table->prepare_items();
?>
<div class="wrap">
<h1>Список данных</h1>
<form method="post">
<?php $table->display(); ?>
</form>
</div>
<?php
}Результат: страница «Данные» в админке содержит таблицу с записями из собственной таблицы БД, с пагинацией и чекбоксами.
При наличии записей в таблице wp_custom_data они отобразятся в виде таблицы с возможностью массового удаления.
Пример 3: Страница с обработкой AJAX-запросов
add_action('admin_menu', function() {
add_menu_page('AJAX demo', 'AJAX demo', 'manage_options', 'ajax-demo', 'ajax_demo_page');
});
function ajax_demo_page() {
?>
<div class="wrap">
<h1>AJAX Demo</h1>
<button id="ajax-button" class="button button-primary">Отправить AJAX запрос</button>
<div id="ajax-response"></div>
</div>
<script>
jQuery(document).ready(function($) {
$('#ajax-button').click(function() {
$.post(ajaxurl, {
action: 'my_ajax_action',
nonce: '<?php echo wp_create_nonce('my_ajax_nonce'); ?>'
}, function(response) {
$('#ajax-response').html(response);
});
});
});
</script>
<?php
}
add_action('wp_ajax_my_ajax_action', 'handle_my_ajax');
function handle_my_ajax() {
check_ajax_referer('my_ajax_nonce', 'nonce');
echo 'Сервер ответил: ' . date('H:i:s');
wp_die();
}Результат: на странице появляется кнопка, по нажатию выполняется AJAX-запрос к WordPress, ответ выводится в блок.
После клика на кнопку в контейнере появляется текущее время сервера.