Contents
Introducción
Este artículo muestra paso a paso cómo crear un panel de ajustes global para WordPress usando la Data API de JavaScript (wp.data). El objetivo es ofrecer una solución moderna y reactiva que almacene la configuración en el servidor mediante REST y que use un store personalizado para integrarse con el ecosistema de Gutenberg y las librerías de WordPress (@wordpress/data, @wordpress/api-fetch, @wordpress/element, @wordpress/components).
Visión general y arquitectura
La arquitectura propuesta incluye:
- Un plugin PHP que registra endpoints REST para obtener y actualizar ajustes (y que protege estos endpoints con capacidades).
- Un script de administración en JavaScript que registra un store con registerStore de @wordpress/data. Ese store usa apiFetch como control para comunicarse con los endpoints REST.
- Un componente React (usando @wordpress/element y hooks de @wordpress/data) que muestra el panel y permite editar y guardar ajustes globales.
- Integración con el menú de administración para mostrar la pantalla de ajustes.
Paso 1 — Crear la estructura del plugin y endpoints REST
Comencemos por el archivo principal del plugin. Aquí registramos dos rutas REST: una para obtener los ajustes y otra para guardarlos. También protegemos las rutas con una comprobación de capacidad (por ejemplo, manage_options).
lt?php / Plugin Name: Mi Panel Global con Data API Description: Ejemplo: panel de ajustes con store de wp.data y REST. Version: 1.0 Author: Tu Nombre / if ( ! defined( ABSPATH ) ) { exit } add_action( rest_api_init, function() { register_rest_route( mi-plugin/v1, /settings, array( methods =gt GET, callback =gt mi_plugin_get_settings, permission_callback =gt function() { return current_user_can( manage_options ) }, ) ) register_rest_route( mi-plugin/v1, /settings, array( methods =gt POST, callback =gt mi_plugin_update_settings, permission_callback =gt function() { return current_user_can( manage_options ) }, args =gt array( settings =gt array( required =gt true, type =gt array, ), ), ) ) } ) function mi_plugin_get_settings( request ) { defaults = array( texto =gt , activar_feature =gt false, color =gt #000000, ) options = get_option( mi_plugin_settings, defaults ) return rest_ensure_response( options ) } function mi_plugin_update_settings( request ) { params = request->get_param( settings ) // Sanitiza según el campo sanitized = array() sanitized[texto] = isset( params[texto] ) ? sanitize_text_field( params[texto] ) : sanitized[activar_feature] = ! empty( params[activar_feature] ) ? true : false sanitized[color] = isset( params[color] ) ? sanitize_hex_color( params[color] ) : #000000 update_option( mi_plugin_settings, sanitized ) return rest_ensure_response( sanitized ) } ?gt
Notas sobre seguridad
- Usamos current_user_can(manage_options) en permission_callback para evitar accesos no autorizados.
- En la función de guardado sanitizamos los campos antes de actualizar la opción.
Paso 2 — Encolar scripts y pasar datos desde PHP
Encolaremos el script de administración y añadiremos dependencias de los paquetes de WordPress. Además pasaremos la URL base de la REST API y el nonce para llamadas seguras (apiFetch puede usarlo).
add_action( admin_enqueue_scripts, function( hook ) { // Solo mostrar en la página del plugin (ejemplo: toplevel_page_mi-plugin) if ( toplevel_page_mi-plugin !== hook ) { return } asset_file = plugin_dir_path( __FILE__ ) . build/index.asset.php ver = file_exists( asset_file ) ? require asset_file : array() wp_enqueue_script( mi-plugin-admin, plugin_dir_url( __FILE__ ) . build/index.js, isset( ver[dependencies] ) ? ver[dependencies] : array( wp-element, wp-data, wp-api-fetch, wp-components, wp-i18n ), isset( ver[version] ) ? ver[version] : filemtime( plugin_dir_path( __FILE__ ) . build/index.js ), true ) wp_localize_script( mi-plugin-admin, miPluginSettings, array( root =gt esc_url_raw( rest_url() ), nonce =gt wp_create_nonce( wp_rest ), ) ) } )
Paso 3 — Registrar la página de administración
Necesitamos una página en el menú para alojar el panel React. El callback de la página imprime un contenedor donde React montará la aplicación.
add_action( admin_menu, function() { add_menu_page( Mi Panel Global, Mi Panel, manage_options, mi-plugin, mi_plugin_render_page, dashicons-admin-generic, 80 ) } ) function mi_plugin_render_page() { // Montaje de la app React echo ltdiv id=mi-plugin-rootgtlt/divgt }
Paso 4 — Crear un store con wp.data (JavaScript)
Registraremos un store que ofrezca acciones para cargar y guardar ajustes. El store usará controles que llaman a apiFetch para interactuar con los endpoints REST definidos en PHP.
/ src/store/index.js / import { registerStore } from @wordpress/data import apiFetch from @wordpress/api-fetch const STORE_NAME = mi-plugin/settings const DEFAULT_STATE = { loaded: false, isSaving: false, settings: { texto: , activar_feature: false, color: #000000, }, } const actions = { fetchSettings() { return { type: FETCH_SETTINGS, } }, receiveSettings( settings ) { return { type: RECEIVE_SETTINGS, settings, } }, saveSettings( settings ) { return { type: SAVE_SETTINGS, settings, } }, savedSettings( settings ) { return { type: SAVED_SETTINGS, settings, } }, } const controls = { FETCH_SETTINGS() { return apiFetch( { path: /mi-plugin/v1/settings } ) }, SAVE_SETTINGS( action ) { return apiFetch( { path: /mi-plugin/v1/settings, method: POST, data: { settings: action.settings }, } ) }, } const reducer = ( state = DEFAULT_STATE, action ) =gt { switch ( action.type ) { case RECEIVE_SETTINGS: return { ...state, loaded: true, settings: action.settings, } case SAVE_SETTINGS: return { ...state, isSaving: true, } case SAVED_SETTINGS: return { ...state, isSaving: false, settings: action.settings, } } return state } const resolvers = { getSettings() { const settings = yield actions.fetchSettings() yield actions.receiveSettings( settings ) }, } const selectors = { getSettings( state ) { return state.settings }, isLoaded( state ) { return state.loaded }, isSaving( state ) { return state.isSaving }, } registerStore( STORE_NAME, { reducer, actions, controls, resolvers, selectors, } )
Puntos clave del store
- controls hace la llamada con apiFetch a nuestras rutas REST. apiFetch automáticamente incluye el nonce si está configurado correctamente por WordPress.
- resolvers permite que useSelect solicite la carga inicial (p. ej. useSelect( (select) =gt select(mi-plugin/settings).getSettings() ) ).
Paso 5 — Construir la interfaz con hooks de @wordpress/data
Un componente React que use useSelect y useDispatch para leer y actualizar los datos del store. Aquí un ejemplo simple con campos de texto, checkbox y color.
/ src/components/SettingsPanel.js / import { useEffect, useState } from @wordpress/element import { useSelect, useDispatch } from @wordpress/data import { TextControl, CheckboxControl, Button, __experimentalPanelColorGradientSettings as ColorPicker } from @wordpress/components export default function SettingsPanel() { const { getSettings, isLoaded, isSaving } = useSelect( ( select ) => { const store = select( mi-plugin/settings ) return { getSettings: store.getSettings, isLoaded: store.isLoaded(), isSaving: store.isSaving(), } }, [] ) // Forzar la carga (resolver) useEffect( () =gt { getSettings() }, [ getSettings ] ) const settings = useSelect( ( select ) =gt select( mi-plugin/settings ).getSettings(), [] ) const { saveSettings } = useDispatch( mi-plugin/settings ) {} const [ local, setLocal ] = useState( settings ) // Mantener local sincronizado cuando los settings se carguen useEffect( () =gt { if ( settings ) { setLocal( settings ) } }, [ settings ] ) if ( ! settings ) { return ltpgtCargando...lt/pgt } const onSave = () =gt { if ( saveSettings ) { saveSettings( local ).then( ( updated ) =gt { // El store ya manejará el estado aquí se puede mostrar notificación si se desea } ) } } return ( ltdivgt ltTextControl label=Texto global value={ local.texto } onChange={ ( value ) =gt setLocal( { ...local, texto: value } ) } /gt ltCheckboxControl label=Activar feature checked={ !! local.activar_feature } onChange={ ( checked ) =gt setLocal( { ...local, activar_feature: checked } ) } /gt ltlabelgtColor principallt/labelgt ltinput type=color value={ local.color } onChange={ ( e ) =gt setLocal( { ...local, color: e.target.value } ) } /gt ltbr /gt ltButton isPrimary onClick={ onSave } isBusy={ isSaving }gtGuardar ajusteslt/Buttongt lt/divgt ) }
Paso 6 — Registrar el store y montar la aplicación
En el archivo principal JS se importa y registra el store, y se monta el componente en el contenedor generado por PHP en la pantalla de administración.
/ src/index.js / import ./store import SettingsPanel from ./components/SettingsPanel import { render } from @wordpress/element import apiFetch from @wordpress/api-fetch // Configurar apiFetch con la URL raíz y nonce (inyectados desde PHP con wp_localize_script) if ( typeof miPluginSettings !== undefined ) { apiFetch.use( ( options, next ) =gt { if ( ! options.headers ) { options.headers = {} } options.headers[ X-WP-Nonce ] = miPluginSettings.nonce return next( options ) } ) } // Montar la app si existe el contenedor document.addEventListener( DOMContentLoaded, () =gt { const root = document.getElementById( mi-plugin-root ) if ( root ) { render( SettingsPanel(), root ) } } )
Opcional: respuestas del store y manejo de promesas
En el ejemplo del store hemos definido controles que devuelven la promesa de apiFetch. Al llamar a las acciones que disparan esos controles, apiFetch resolverá con los datos. Puedes encadenar .then en el dispatch o confiar en que el store actualice su estado y los selectores reflejen el nuevo valor.
Cómo funcionan las piezas juntas
- El componente monta y llama al selector/resolver para cargar ajustes: el resolver ejecuta el control que hace apiFetch GET a /mi-plugin/v1/settings.
- Al editar y pulsar Guardar, el componente llama a la acción saveSettings que dispara al control SAVE_SETTINGS este hace apiFetch POST con los datos al endpoint REST.
- El endpoint PHP valida, sanitiza y actualiza la opción en la base de datos (update_option) y retorna la configuración actualizada.
- El control devuelve la respuesta, el store despacha SAVED_SETTINGS y el selector reflecta los nuevos valores la UI reacciona automáticamente.
Buenas prácticas y recomendaciones
- Sanitiza y valida siempre los datos en el servidor. No confíes únicamente en validación cliente.
- Protege los endpoints REST con permission_callback y capacidades adecuadas.
- Usa wp_create_nonce(wp_rest) y pásalo al cliente apiFetch lo incluirá en la cabecera X-WP-Nonce.
- Si tienes muchos ajustes o tipos complejos, considera usar register_setting y REST schema, o bien endpoints por secciones.
- Evita bloquear la UI durante llamadas largas utiliza estados de carga y mensajes de error claros.
- Si se necesita compatibilidad con multi-site, maneja la lectura/escritura con get_site_option/update_site_option según corresponda.
Ejemplo final de flujo de trabajo resumido
- Usuario abre la página de ajustes en admin.
- El JavaScript monta el componente y el store solicita los datos (GET).
- El servidor devuelve los ajustes guardados con get_option.
- El usuario edita y pulsa Guardar el componente llama a la acción save que ejecuta apiFetch POST.
- El servidor valida y guarda con update_option y devuelve el resultado.
- El store actualiza su estado y la UI muestra los cambios.
Conclusión
Crear un panel de ajustes global con la Data API de WordPress aporta una experiencia reactiva y coherente con el ecosistema de bloques y herramientas modernas. Separando responsabilidades (endpoints REST para persistencia, store de wp.data para gestión de estado y componentes React para UI) se consigue una solución escalable, testable y mantenible.
Recursos útiles
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |