Uso de opciones para almacenar datos en el editor de bloques de WordPress (Gutenberg)
Anteriormente exploramos el almacenamiento de datos del editor de bloques de WordPress (Gutenberg) en los atributos de bloque y en la publicación meta, pero ¿sabía que puede almacenar y recuperar desde la tabla de opciones de WordPress importando apidesde @wordpress/api.
En esta guía, echamos un vistazo a lo que escribirías clásicamente en PHP como update_optiony get_option.
Para implementar esto, debemos aprovechar el ciclo de vida de React, por lo que veremos cómo crear un componente de React importando Componentdesde @wordpress/element.
requisitos previos
- Familiarícese con la creación de complementos para WordPress Gutenberg
- Sea familiar con los bloques dinámicos y la representación del lado del servidor
- Comprenda cómo puede crear metaboxes en Gutenberg
Ese último requisito es útil para la interfaz de usuario que vamos a usar en esta guía; sin embargo, en aplicaciones del mundo real, es probable que use este método en una barra lateral o en una página de opciones.
Registrar las Opciones en PHP
Antes de que podamos usar una opción en JavaScript, debemos asegurarnos de que la hayamos registrado en PHP usando register_settingy que el show_in_restargumento se haya establecido en verdadero.
Siguiendo con la guía de Dynamic Block, abra el archivo PHP raíz del complemento (en este caso wholesome-plugin.php) y agregue el siguiente código al final de ese archivo después de todas las demás funciones:
function wholesomecode_wholesome_plugin_register_settings() {
register_setting(
'wholesomecode_wholesome_plugin_settings',
'wholesomecode_wholesome_plugin_example_text',
[
'default' => '',
'show_in_rest' => true,
'type' => 'string',
]
);
}
add_action( 'init', 'wholesomecode_wholesome_plugin_register_settings' );
Este código registra un metacampo llamado wholesomecode_wholesome_plugin_block_textpara el wholesomecode_wholesome_plugin_settingsgrupo de opciones. También garantiza que la API REST pueda acceder a este metacampo con el show_in_restvalor establecido en verdadero.
Crear el componente
Abra el /src/edit.jsarchivo, vamos a alterar un poco la estructura de este archivo para que podamos generar nuestro archivo Component.
Corte y pegue la totalidad de este bloque de código en el /src/edit.jsarchivo, cubriremos lo que hace en un momento:
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import {
Panel,
PanelBody,
TextControl,
} from '@wordpress/components';
import { Component } from '@wordpress/element';
import './editor.scss';
class OptionsExample extends Component {
constructor() {
super( ...arguments );
this.state = { exampleText: '' };
}
render() {
const { exampleText } = this.state;
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesome-plugin') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
</PanelBody>
</Panel>) }
}
export default function Edit( props) {
return (<div { ...useBlockProps() }>
<OptionsExample { ...props }/>
</div>
);
}
Puede reconocer que la interfaz de usuario que hemos implementado es exactamente la misma de la guía Gutenberg Meta Box, donde usamos meta atributos de publicación. También puede notar que todavía no estamos obteniendo o configurando información usando opciones, y en su lugar solo estamos usando el componente state.
Estado de uso
La razón por la que creamos un componente personalizado y luego lo pasamos a nuestra Editfunción es para que podamos aprovechar el estado. Hemos hecho esto porque:
- Crearemos una función para cargar las opciones de la API, y debemos almacenar esto en el estado para que nuestros componentes puedan leerlo.
- No queremos que la API actualice las opciones tan pronto como cambie el texto en nuestro cuadro de texto, por lo que necesitamos una función para guardar el estado en las opciones a través de la API una vez que se haya guardado la publicación.
Usar el estado es bastante simple. En el constructor inicializamos el estado así:
this.state = { exampleText: '' };
Y en el componente accedemos a él de forma similar a como hemos accedido a los atributos en la guía anterior:
const { exampleText } = this.state;
La diferencia es que, en nuestro onChangemétodo, en lugar de usar setAttributes, usamos this.setState.
Obtener opciones de la API
En la parte superior del documento importar apidesde @wordpress/api:
import api from '@wordpress/api';
Agregue una nueva propiedad donde se inicializa el estado de isAPILoaded. Necesitaremos esto para asegurarnos de que no intentamos acceder a la API o renderizar el componente antes de que se haya cargado la API.
this.state = {
exampleText: '',
isAPILoaded: false,
};
Ahora dentro del Componente que creamos, agregue un bloque de código bajo el constructor llamado componentDidMount. Este es un método de ciclo de vida de React, que se llama después de que se ha agregado un componente al DOM.
En ese bloque de código agregue lo siguiente:
componentDidMount() {
api.loadPromise.then(() => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
isAPILoaded: true,
} );
} );
}
} );
}
Aquí estamos accediendo a la opción que registramos anteriormente con la register_settingfunción.
Este bloque de código hace lo siguiente:
- Obtiene la configuración de la API de configuración de Guttenberg de WordPress.
- Obtiene
isAPILoadeddel estado - Si la API no se ha cargado, obtiene la configuración de la API en un
response - Luego configuramos el estado para actualizar el estado con la opción a la que queremos acceder y establecemos el
isAPILoadedestado en verdadero
Detener la representación de bloques sin configuración
No queremos que nuestro bloque se represente antes de que se hayan completado las configuraciones. Para encargarnos de esto, podemos importar un PlaceHolder y un Spinner desde $wordpress/components:
import {
Panel,
PanelBody,
Placeholder,
Spinner,
TextControl,
} from '@wordpress/components';
Luego, en el método de representación del componente, asegúrese de obtener isAPILoadeddel estado y genere el Placeholdery Spinnersi no es así:
const {
exampleText,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
De esta forma, si las opciones no se han cargado, obtenemos un bonito marcador de posición hasta que se carga el componente:
Marcador de posición y Spinner
Conexión con Gutenberg en Save
Ahora que estamos leyendo las opciones de la tabla de opciones, necesitamos una forma de guardar esas opciones cuando las cambiamos. Para ello vamos al subscribealmacén de datos de WordPress Gutenberg, que nos indicará cuando algo haya cambiado.
Al usar esto, crearemos un oyente para cuando se guarde la publicación y guardaremos nuestra configuración cuando eso suceda.
Para ello importa subscribey selectdesde @wordpress/data.
import { select, subscribe } from '@wordpress/data';
Luego, en la parte superior del componentDidMountbloque de código, escribe lo siguiente:
subscribe(() => {
const { exampleText } = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
} );
settings.save();
});
El código hace lo siguiente:
- Comprueba si la publicación se está guardando
- Verifique si el guardado es un guardado automático
- Si la publicación se está guardando y no se guarda automáticamente, inserte la nueva configuración en la API de configuración
- Active un guardado de la API de configuración.
Un pequeño truco
Podríamos dejar nuestro código así, pero debido a que estamos colocando nuestra configuración en un bloque, y no en una barra lateral u otro componente del editor, si cambiamos una de las opciones, y nada más en el editor, el botón ‘guardar’ no volverse activo.
Esto se debe a que no estamos usando setAttributesni nada para alterar el código real del bloque.
Podemos solucionar esto simplemente editando otra parte de la publicación o agregando un pequeño truco en el TextControlcódigo:
onChange={ (exampleText) => { this.setState( { exampleText } ); setAttributes( { exampleText }) } }
Recordando poner esta línea de código en la parte superior del método de renderizado para extraer setAttributesdel props(debido a que estamos usando un Componente, accedemos a los accesorios ligeramente diferentes con this.
const { setAttributes } = this.props;
Ahora, cuando cambiamos nuestro atributo, un atributo ‘falso’ cambiará, lo que hará que el editor piense que ahora podemos guardar la publicación.
Es un poco raro, pero para este caso de uso hace lo que necesitamos.
EditEl código completo
Aquí está todo el código que necesita para el Editmétodo:
yo ` import {} de ‘ @wordpress /i18n’; importar api desde ‘ @wordpress /api’; importar { useBlockProps } desde ‘ @wordpress /block-editor’; import { Panel, PanelBody, Placeholder, Spinner, TextControl, } from ‘ @wordpress /components’; importar { seleccionar, suscribirse } desde ‘ @wordpress /data’; importar {Componente} desde ‘ @wordpress /element’;
importar ‘./editor.scss’;
clase OpcionesEjemplo extiende Componente { constructor() { super( …argumentos );
this.state = {
exampleText: '',
isAPILoaded: false,
};
}
componentDidMount() {
subscribe( () => {
const { exampleText } = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
} );
settings.save();
});
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleText,
isAPILoaded,
} = this.state;
const { setAttributes } = this.props;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesomecode') }
icon="admin-plugins"
>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => { this.setState( { exampleText } ); setAttributes( { exampleText }) } }
value={ exampleText }
/>
</PanelBody>
</Panel>) }
}
Exportar función predeterminada Editar (accesorios) { volver (
); }
### Remove the Attributes
Option up `src/index.js` and remove the attributes block that we placed there in the previous guides. We are not storing any attributes, the data will be pushed into and retrieved from the options table.
Render the Output
Because we have saved our attribute as settings in the WordPress options table, we could output this anywhere in WordPress using `get_option`:
get_option(‘wholesomecode_wholesome_plugin_block_text’, " );
Continuing from the [Dynamic Block guide](https://wholesomecode.ltd/guides/php-render-block-wordpress-gutenberg/), let's see how we can access this attribute on the server side in PHP.
With this in mind, let’s update our `register_block_type` to output the option:
register_block_type( ‘wholesomecode/wholesome-plugin’, array( ‘editor_script’ => ‘wholesomecode-wholesome-plugin-block-editor’, ‘editor_style’ => ‘wholesomecode-wholesome-plugin-block-editor’, ‘render_callback’ = > function ($atributos, $contenido) { $example_text = get_option( ‘wholesomecode_wholesome_plugin_example_text’ ); return "
$ejemplo_texto
"; }, ‘style’ => ‘wholesomecode-wholesome-plugin-block’,) );
Note that we no longer need to register the `attributes` here, because we are only accessing the post meta field via the `get_post_meta` function.
5.
Using the Block
--------------------
Putting it all together, let’s see the block in action:
:
importar {} de ‘ @wordpress /i18n’; importar api desde ‘ @wordpress /api’; importar { useBlockProps } desde ‘ @wordpress /block-editor’; import { Panel, PanelBody, PanelRow, Placeholder, SelectControl, Spinner, TextControl, ToggleControl, } from ‘ @wordpress /components’; importar { seleccionar, suscribirse } desde ‘ @wordpress /data’; importar {Componente} desde ‘ @wordpress /element’;
importar ‘./editor.scss’;
clase OpcionesEjemplo extiende Componente { constructor() { super( …argumentos );
this.state = {
exampleSelect: '',
exampleText: '',
exampleText2: '',
exampleText3: '',
exampleToggle: false,
isAPILoaded: false,
};
}
componentDidMount() {
subscribe( () => {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
} = this.state;
const isSavingPost = select('core/editor').isSavingPost();
const isAutosavingPost = select('core/editor').isAutosavingPost();
if (isAutosavingPost) {
return;
}
if (! isSavingPost) {
return;
}
const settings = new api.models.Settings( {
[ 'wholesomecode_wholesome_plugin_example_select' ]: exampleSelect,
[ 'wholesomecode_wholesome_plugin_example_text' ]: exampleText,
[ 'wholesomecode_wholesome_plugin_example_text_2' ]: exampleText2,
[ 'wholesomecode_wholesome_plugin_example_text_3' ]: exampleText3,
[ 'wholesomecode_wholesome_plugin_example_toggle' ]: exampleToggle,
} );
settings.save();
});
api.loadPromise.then( () => {
this.settings = new api.models.Settings();
const { isAPILoaded } = this.state;
if (isAPILoaded === false) {
this.settings.fetch().then( (response) => {
this.setState( {
exampleSelect: response[ 'wholesomecode_wholesome_plugin_example_select' ],
exampleText: response[ 'wholesomecode_wholesome_plugin_example_text' ],
exampleText2: response[ 'wholesomecode_wholesome_plugin_example_text_2' ],
exampleText3: response[ 'wholesomecode_wholesome_plugin_example_text_3' ],
exampleToggle: Boolean( response[ 'wholesomecode_wholesome_plugin_example_toggle' ] ),
isAPILoaded: true,
} );
} );
}
} );
}
render() {
const {
exampleSelect,
exampleText,
exampleText2,
exampleText3,
exampleToggle,
isAPILoaded,
} = this.state;
if (! isAPILoaded) {
return (<Placeholder>
<Spinner />
</Placeholder>
);
}
return (<Panel>
<PanelBody
title={ __( 'Example Meta Box', 'wholesome-plugin') }
icon="admin-plugins"
>
<SelectControl
help={ __( 'An example dropdown field.', 'wholesome-plugin') }
label={ __( 'Example Select', 'wholesome-plugin') }
onChange={ (exampleSelect) => this.setState( { exampleSelect }) }
options={ [
{
label: __( 'Please Select...', 'wholesome-plugin' ),
value: '',
},
{
label: __( 'Option 1', 'wholesome-plugin' ),
value: 'option-1',
},
{
label: __( 'Option 2', 'wholesome-plugin' ),
value: 'option-2',
},
] }
value={ exampleSelect }
/>
<TextControl
help={ __( 'This is an example text field.', 'wholesome-plugin') }
label={ __( 'Example Text', 'wholesome-plugin') }
onChange={ (exampleText) => this.setState( { exampleText }) }
value={ exampleText }
/>
<PanelRow>
<TextControl
help={ __( 'Use PanelRow to place controls inline.', 'wholesome-plugin') }
label={ __( 'Example Text 2', 'wholesome-plugin') }
onChange={ (exampleText2) => this.setState( { exampleText2 }) }
value={ exampleText2 }
/>
<TextControl
help={ __( 'This control is inline.', 'wholesome-plugin') }
label={ __( 'Example Text 3', 'wholesome-plugin') }
onChange={ (exampleText3) => this.setState( { exampleText3 }) }
value={ exampleText3 }
/>
</PanelRow>
<ToggleControl
checked={ exampleToggle }
help={ __( 'An example toggle.', 'wholesome-plugin') }
label={ __( 'Example Toggle', 'wholesome-plugin') }
onChange={ (exampleToggle) => this.setState( { exampleToggle }) }
/>
</PanelBody>
</Panel>) }
}
Exportar función predeterminada Editar (accesorios) { volver (
); } `
Aquí está el resultado:
Opciones adicionales
- Eche un vistazo a la creación de bloques secundarios anidados con el
InnerBlockscomponente - Eche un vistazo al uso de metacampos de publicación en bloques de Gutenberg
- Eche un vistazo a la creación de metaboxes personalizados en Gutenberg