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 🙂 |
