Una clase auxiliar para WordPress que simplifica y robustece la creación de menús y submenús en el panel de administración, con soporte para múltiples estrategias de renderizado y validación integrada.
WPAdminMenu es una clase PHP orientada a objetos diseñada para abstraer la complejidad del sistema de menús de WordPress (add_menu_page, add_submenu_page) y ofrecer una API más segura, flexible y expresiva. Está pensada para desarrolladores de plugins o temas que necesitan registrar interfaces de administración personalizadas sin repetir lógica ni exponerse a errores comunes como slugs duplicados, rutas de vista inexistentes o callbacks mal definidos.
La clase proporciona:
- Registro seguro de menús y submenús, con verificación contra el estado global de WordPress.
- Callbacks flexibles: acepta funciones, closures, rutas a archivos PHP o arrays estructurados con datos.
- Generación automática de slugs únicos basados en el título del menú.
- Validación proactiva mediante
_doing_it_wrong()para advertir sobre errores de uso en tiempo de desarrollo. - Soporte para menús nativos de WordPress como padres (por ejemplo,
tools.php,options-general.php). - Acceso a metadatos internos, como el hook suffix generado por WordPress, útil para integrar scripts, estilos o acciones específicas.
Todo el estado se mantiene en memoria durante la ejecución del request (sin caché ni persistencia), lo que garantiza ligereza y previsibilidad.
Usa add_menu() para crear un nuevo ítem en la barra lateral del panel de administración. La clase se encarga de:
- Generar un slug único si no se proporciona.
- Verificar que el slug no esté ya en uso por WordPress, otro plugin o tema.
- Normalizar el callback de renderizado según el tipo de entrada.
Con add_submenu(), puedes añadir páginas bajo un menú existente, ya sea uno creado por ti o un menú nativo de WordPress. La clase valida que el menú padre exista antes de registrar el submenú.
El parámetro $callback admite múltiples formatos:
| Tipo | Ejemplo | Comportamiento |
|---|---|---|
| Callable | function() { echo 'Hola'; } |
Se ejecuta directamente. |
| Ruta de archivo | __DIR__ . '/views/dashboard.php' |
Incluye el archivo si existe; si no, muestra error. |
| Array estructurado | ['view' => 'ruta.php', 'data' => ['user' => $user]] |
Incluye la vista y extrae las variables de data con extract(). |
| Nulo o inválido | null |
Muestra una página predeterminada indicando que no se definió contenido. |
⚠️ Nota de seguridad: El uso deextract()está limitado a datos controlados por el desarrollador, por lo que se considera seguro en este contexto.
Antes de registrar cualquier menú, la clase verifica:
- Si el slug ya fue registrado por esta misma instancia.
- Si el slug ya existe en las variables globales
$menuo$submenude WordPress. - Si el menú padre (en submenús) es válido (registrado por esta clase, es un slug nativo de WP o existe en
$menu/$submenu).
En caso de conflicto, no se registra el menú duplicado y se emite una advertencia mediante _doing_it_wrong(), facilitando la depuración.
- Devuelve el hook suffix generado por
add_menu_page()oadd_submenu_page(), permitiendo usaradd_action("load-{$hook_suffix}", ...)oadd_action("admin_print_styles-{$hook_suffix}", ...). - Usa
sanitize_title()para generar slugs seguros. - Soporta traducciones mediante un text domain configurable.
- WordPress 5.0+
- PHP 7.4 o superior
Coloca el archivo que contiene la clase en tu proyecto (plugin o tema) y asegúrate de que esté bajo el namespace WPAdminMenuHelper\Admin.
use WPAdminMenuHelper\Admin\WPAdminMenu;
// Inicializa el helper (opcional: define tu propio text domain)
$menu = new WPAdminMenu('mi-plugin');
// Menú principal con vista y datos
$main_slug = $menu->add_menu(
page_title: 'Panel de Mi Plugin',
menu_title: 'Mi Plugin',
capability: 'manage_options',
menu_slug: null, // se generará automáticamente
callback: [
'view' => __DIR__ . '/views/admin-main.php',
'data' => [
'settings' => get_option('mi_plugin_settings'),
'nonce' => wp_create_nonce('mi_plugin_nonce')
]
],
icon_url: 'dashicons-admin-generic'
);
// Submenú bajo el menú anterior
$menu->add_submenu(
parent_slug: $main_slug,
page_title: 'Herramientas de Mi Plugin',
menu_title: 'Herramientas',
capability: 'manage_options',
menu_slug: 'mi-plugin-tools',
callback: __DIR__ . '/views/tools.php'
);
// Submenú bajo un menú nativo de WordPress
$menu->add_submenu(
parent_slug: 'tools.php',
page_title: 'Exportar Datos',
menu_title: 'Exportar (Mi Plugin)',
capability: 'manage_options',
callback: function() {
echo '<div class="wrap"><h1>Exportar</h1></div>';
}
);if ($menu->has_menu('mi-plugin-tools')) {
$hook = $menu->get_hook_suffix('mi-plugin-tools');
add_action("load-{$hook}", function() {
// Lógica específica al cargar esta página
});
}| Método | Descripción |
|---|---|
add_menu(...) |
Registra un menú principal. Devuelve el slug usado. |
add_submenu(...) |
Registra un submenú. Devuelve el slug usado. |
get_hook_suffix(string $menu_slug): ?string |
Obtiene el hook suffix de WordPress para ese menú. |
has_menu(string $menu_slug): bool |
Verifica si un menú fue registrado por esta instancia. |
get_registered_menus(): array |
Devuelve todos los menús registrados (con metadatos). |
- No se permite el registro de slugs duplicados, incluso si provienen de otros plugins. Esto evita colisiones silenciosas.
- No se realiza caché: todos los menús se registran en tiempo real durante la ejecución del hook de WordPress (normalmente
admin_menu). - Las vistas deben ser archivos PHP válidos. Si la ruta no existe, se muestra un mensaje de error claro en lugar de fallar silenciosamente.
- El helper no gestiona permisos más allá de la capacidad (
$capability). Es responsabilidad del desarrollador asegurar que las vistas y callbacks respeten la lógica de autorización.
Este código es de dominio público o se distribuye bajo la licencia GPL, a menos que se especifique lo contrario en tu proyecto. Puedes usarlo, modificarlo y redistribuirlo libremente.
Diseñado para desarrolladores que valoran la claridad, la seguridad y la separación de responsabilidades en la capa de vista del panel de administración de WordPress.