Skip to main content

Cómo consultar usuarios en Gutenberg

Publicado hace
Actualizado hace
15 minutos de lectura

Hace algunos meses me vi en la necesidad de crear un pequeño bloque en Gutenberg que hiciera una consulta para conseguir datos de usuarios en WordPress. Me sorprendí un poco de lo poco documentada que está esta sección así que he decidio escribir un pequeño tutorial mostrando cómo es que logré consultar usuarios en Gutenberg.

Para que las cosas sean más sencillas de digerir, escribiré un pequeño bloque que simplemente dejará seleccionar a un autor en el sitio y mostrará su información. Comencemos:

TABLA DE CONTENIDO

PASO 1: LA BASE DE EL BLOQUE

Comenzaremos con la creación básica del bloque, no voy a profundizar mucho en este tema porque realmente no es el punto de este tutorial, pero para hacer las cosas más sencillas utilizaré la herramienta oficial de WordPress @wordpress/create-block:

Simplemente necesitamos ejecutar estos comandos (slug es el nombre del directorio donde se creará el plugin).

1npx @wordpress/create-block@latest mah-users --wp-env
2cd mah-users
3npx wp-env start
4npm start
5
Nota

Utilizando wp-env

La bandera wp-env nos permite crear un ambiente local en el que funcionará nuestro plugin, si ya tienes un ambiente que usas para pruebas esto no es necesario.

Si no eres muy familiar con ambientes locales te invito a ver mi video Creando un ambiente local para WordPress con WP-ENV en youtube donde explico una manera fácil de lograrlo con las herramientas que ofrece WordPress.

Una vez que tenemos nuestro plugin funcionando, podemos comenzar a crear el bloque.

PASO 2: LA ESTRUCTURA

Como mencioné anteriormente, lo que queremos es crear una selección de usuarios, y que al seleccionarlos se muestren los datos del usuario seleccionado, así que para ello necesitaremos sólo un atributo, que será el ID del usuario que queremos mostrar:

Entonces en nuestro block.json agregamos el atributo:

1"attributes": {
2 "userId": {
3 "type": "number"
4 }
5}
6
Advertencia

Este tutorial esta pensado para ser un bloque dinámico (dynamic block), por lo que sólo mostraremos el funcionamiento del editor de bloque. No entraré a fondo sobre cómo se muestra un bloque dinámico en el frontend, pero si quieres que lo haga déjame un comentario y lo veremos en un futuro.

PASO 2.1: LA SELECCIÓN

Como necesitamos seleccionar usuarios, podemos utilizar uno de los componentes que ya existen en WordPress para ello, así que en el archivo edit.js podemos importar el componente llamado SelectControl y mostrarlo en el editor:

Para importamos el componente al principio del archivo:

1import { SelectControl } from '@wordpress/components';
2

Y dentro de la función Edit lo regresamos:

1export default function Edit() {
2 return (
3 <div { ...useBlockProps() }>
4 <SelectControl
5 label={ __( 'Selecciona un usuario', 'mah-users' ) }
6 value={ 'user1' }
7 options={ [
8 { label: 'Usuario 1', value: 'user1' },
9 { label: 'Usuario 2', value: 'user2' },
10 { label: 'Usuario 3', value: 'user3' },
11 ] }
12 />
13 </div>
14 );
15}
16
Consejo

Si estás siguiendo este tutorial y te preguntas por qué el select se ve azul… Es porque create-block agrega algunos estilos por defecto a los archivos, para eliminarlos simplemente elimina el contenido de los archivos style.scss y editor.scss.

Una vez hecho esto, tendremos algo similar a esta imagen:

Ejemplo de select box

PASO 2.2: LOS DATOS REALES

Nuestro select box está funcionando, pero es claro que no como queremos, para empezar, los datos no nos dicen nada, lo primero que debemos hacer, es hacer una consulta a la base de datos para que muestre las opciones con base en los datos que existen en nuestro sitio.

Entonces, podemos hacer uso del método getUsers que está disponible en el paquete @wordpress/core-data.

Para empezar, debemos importar nuestros componentes, regresamos al principio del archivo y agregamos lo siguiente:

1import { store as coreStore } from '@wordpress/core-data';
2import { useSelect } from '@wordpress/data';
3

Y hacemos una llamada select para tener nuestra consulta, esto es dentro de la función Edit:

1const records = useSelect( ( select ) => {
2 const { getUsers } = select( coreStore );
3
4 return getUsers( {
5 per_page: -1, // Seleccionar a todos los usuarios.
6 _fields: 'id,name', // Solo regresa el ID, y el Nombre.
7 context: 'view', // Indica que solo queremos ver los datos, no editarlos. Esto reduce los campos a sólo lo que necesitemos.
8 } );
9}, [] );
10
Precaución

Cuando usamos per_page: -1 lo que estamos haciendo es decirle a WordPress que queremos consultar todos los registros. Esto puede llegar a ser problemático si tenemos por ejemplo una red de blogs con cientos (o miles) de usuarios.

Pero generalmente, ese no es el caso así que para propósitos de este tutorial asumiremos que es seguro utilizarlo.

Si hacemos un console.log con el contenido de records notaremos que eventualmente tendremos lo siguiente:

1[
2 {
3 "id": 1,
4 "name": "admin"
5 }
6]
7

Esto es casi lo que necesitamos, ¿por qué "casi"? porque el formato es erróneo, al ver la documentación de SelectControl encontramos que cada opción debe ser un objeto con un label, y un value. En este caso nuestro value (lo que vamos a actualizar en los atributos del bloque) va a ser el ID de usuario, mientras que el label va a ser el nombre del usuario.

Así que para conseguirlo debemos hacer un pequeño loop a través de los resultados, y ya que estamos en esto utilizaremos useMemo para que quede guardado en la memoria y no se tenga que repetir hasta que tengamos nuevos resultados.

Primero importamos la función al inicio del archivo:

1import { useMemo } from '@wordpress/element';
2

Y justo en seguida de nuestra consulta, convertimos el formato a lo que necesitamos:

1const records = useSelect( ( select ) => {
2 const { getUsers } = select( coreStore );
3
4 return getUsers( {
5 per_page: -1, // Seleccionar a todos los usuarios.
6 _fields: 'id,name', // Solo regresa el ID, y el Nombre.
7 context: 'view', // Indica que solo queremos ver los datos, no editarlos. Esto reduce los campos a sólo lo que necesitemos.
8 } );
9}, [] );
10
11const users = useMemo( () => {
12 const fetchedUsers = ( records ?? [ { id: '', name: __( 'Cargando…', 'mah-users' ) } ] ).map( ( record ) => {
13 return {
14 value: record.id,
15 label: record.name,
16 };
17 } );
18
19 return fetchedUsers;
20}, [ records ] );
21

Si una vez más intentamos un console.log, lo que veremos será el formato que necesitamos, ahora simplemente reemplazamos las opciones que teníamos en SelectControl por nuestra variable users:

1[
2 {
3 "value": 1,
4 "label": "admin"
5 }
6]
7
1<SelectControl
2 label={ __( 'Selecciona un usuario', 'mah-users' ) }
3 value={ 'user1' }
4 options={ users }
5/>
6

Y el resultado será el siguiente:

Consulta exitosa

Si queremos tener una mejor idea que cómo se vería esto con un nombre real, sólo debemos editar el perfil de usuario y agregar nuestros datos.

Consejo

Si queremos saber cómo funcionaría con más usuarios, hay una forma sencilla de conseguirlo. wp-env viene con una versión de wp-cli lista para utilizarse, y como se menciona en mi Introducción a WP-CLI, hay un comando que nos permite agregar nuevos usuarios fácilmente. Necesitamos crear un archivo CVS con una lista de usuarios, por ejemplo users.cvs en la raíz de nuestra carpeta mah-users:

1user_login,user_email,display_name,role
2bobjones,bobjones@example.com,Bob Jones,contributor
3newuser1,newuser1@example.com,New User,author
4existinguser,existinguser@example.com,Existing User,administrator
5johndoe,johndoe@example.com,John Doe,subscriber
6janedoe,janedoe@example.com,Jane Doe,subscriber
7

Ahora lo guardamos y utilizamos el siguiente comando en la terminal:

1npx wp-env run cli wp user import-csv wp-content/plugins/mah-users/users.csv
2

Después de agregar algunos usuarios extra a nuestro sitio, ahora nuestro resultado es el esperado:

Lista de usuarios completa

PASO 3: GUARDANDO LA SELECCIÓN

Guardar la selección es de hecho bastante sencillo, como ya agregamos el atributo en el que guardaremos la selección al principio de este tutorial, sólo necesitamos agregar una función al prop onChange de SelectControl (mucho buzzword…).

Primero especificamos que vamos a utilizar los props attributes, y setAttributes en la función Edit de nuestro bloque:

1export default function Edit( { attributes, setAttributes } ) {
2

Después reemplazamos el value por el atributo userId y agregamos una función para que cuando el valor cambie, se actualice el atributo por el valor seleccionado… algo difícil de explicar pero básicamente se verá así en código:

1<SelectControl
2 label={ __( 'Select a user', 'mah-users' ) }
3 value={ attributes.userId }
4 options={ users }
5 onChange={ ( userId ) => setAttributes( { userId: parseInt( userId, 10 ) } ) }
6/>
7
Nota

¿parseInt?

En el ejemplo anterior utilizo parseInt para convertir el nuevo valor a un entero, esto porque comencé a tener problemas con que el valor no se guardaba.

La razón era que estaba intentando guardarse como un string cuando en el attributo especificamos que éste iba a ser un number.

Finalmente, podemos probarlo al seleccionar un usuario del dropdown, guardamos el post y si recargamos la página, deberíamos ver el usuario que elegimos aparecer automáticamente seleccionado.

PASO 4: MOSTRAR LA INFORMACIÓN DEL USUARIO

Aquí tenemos una decisión que tomar, por una parte, podríamos terminar nuestro editor aqui (que sería totalmente aceptable) y avanzar a la parte del frontend, pero como nos preocupamos por la experiencia del usuario, ¿por qué no mostrar una vista previa al editor cuando seleccionamos a un usuario?

La idea sería:

  1. Mostrar el dropdown si ningún usuario ha sido seleccionado
  2. Mostrar una vista previa de los datos del usuario que se selecciona
  3. Ofrecer controles para cambiar de usuario desde la vista previa
  4. Ofrecer una forma de eliminar la selección y volver al dropdown

PASO 4.1: MOSTRAR EL DROPDOWN SI NINGÚN USUARIO HA SIDO SELECCIONADO

El primer paso prácticamente ya lo tenemos, ya estamos mostrando nuestro dropdown por defecto, esto, dentro de Gutenberg, es lo que conocemos como "placeholder". En práctica se muestra cuando ninguna selección ha sido hecha, y desaparece cuando algo se selecciona, pero no funciona así por defecto, así que hagámoslo funcionar:

Para comenzar, y para seguir mejores prácticas, utilizaremos el componente Placeholder de Gutenberg desde el mismo paquete de donde importamos SelectControl.

1import { SelectControl, Placeholder } from '@wordpress/components';
2import { people } from '@wordpress/icons';
3

Después, especificamos que queremos que se muestre nuestro dropdown sólo si no se ha seleccionado un usuario, y lo envolvemos en el <Placeholder />.

1if ( ! attributes.userId ) {
2 return (
3 <div { ...useBlockProps() }>
4 <Placeholder icon={ people } label={ __( 'Usuarios', 'mah-users' ) }>
5 <SelectControl
6 label={ __( 'Selecciona un usuario', 'mah-users' ) }
7 value={ attributes.userId }
8 options={ users }
9 onChange={ ( userId ) => setAttributes( { userId: parseInt( userId, 10 ) } ) }
10 />
11 </Placeholder>
12 </div>
13 );
14}
15
16return (
17 <div { ...useBlockProps() } >
18 { __( 'Se ha seleccionado un usuario', 'mah-users' ) }
19 </div>
20);
21

Ahora, si recargamos la página veremos que como ya hemos seleccionado y guardado un usuario anteriormente, tendremos este resultado:

Usuario seleccionado

Para ver nuestro placeholder, sólo necesitamos seleccionar el block, eliminarlo, e insertar uno nuevo:

Placeholder
Consejo

Notarás que estamos importando un ícono para mostrar en nuestro placeholder, si quieres saber cuales están disponibles puedes darte una vuelta por mi Buscador de íconos de Gutenberg

Paso 4.2 MOSTRAR UNA VISTA PREVIA DE LOS DATOS DEL USUARIO QUE SE SELECCIONA

Ahora que tenemos diferentes componentes para mostrar, debemos agregar una vista previa de los datos del usuario para que el editor sepa qué es lo que está agregando, así que para comenzar, debemos hacer un request para recibir los datos cada vez que el usuario es seleccionado.

Para esto deberemos agregar un nuevo bloque de useSelect que utilice userId como dependencia, de esta forma podremos conseguir el nombre del usuario, su avatar, y su descripción.

1const { userName, userAvatar, userDescription } = useSelect( ( select ) => {
2 const { getEntityRecord } = select( coreStore );
3 const userInfo = getEntityRecord( 'root', 'user', attributes.userId );
4
5 return {
6 userName: userInfo?.name,
7 userAvatar: userInfo?.avatar_urls?.['96'],
8 userDescription: userInfo?.description,
9 };
10}, [ attributes.userId ] );
11

Y para mostrarlo debemos reemplazar nuestro div que contiene "Se ha seleccionado un usuario" por un bloque con los datos que acabamos de recibir:

Consejo

Si no queremos meternos mucho en el tema del diseño, Gutenberg ofrece un par de componentes para ayudarnos a maquetar, que utilizaré en el siguiente ejemplo, estos son Flex y FlexItem

Importamos los nuevos componentes:

1import { Flex, FlexItem, SelectControl, Placeholder } from '@wordpress/components';
2

Y dentro de la función Edit:

1return (
2 <div { ...useBlockProps() } >
3 <Flex justify='start' align='start' gap="4">
4 <FlexItem>
5 <img src={ userAvatar } alt={ userName } />
6 </FlexItem>
7 <FlexItem isBlock>
8 <h3>{ userName }</h3>
9 <p>{ userDescription }</p>
10 </FlexItem>
11 </Flex>
12 </div>
13);
14

Notarás que lo único que vemos es el nombre, así que debemos actualizar los datos del perfil con información real (recuerda que el avatar está ligado a Gravatar), y una vez guardemos los datos podremos ver esto:

Preview result

PASO 4.3: OFRECER CONTROLES PARA CAMBIAR DE USUARIO DESDE LA VISTA PREVIA

Ya tenemos nuestra vista previa, pero nos encontramos con un problema, y es que si queremos cambiar de usuario ahora, deberemos borrar y volver a crear nuestro bloque. Eso no es muy amigable, así que lo que haremos es agregar un control a nuestro sidebar para cambiar fácilmente de usuario.

Básicamente copiaremos nuestro SelectControl y lo pegaremos en los controles, así que para eso importaremos InspectorControls, y PanelBody y los agregaremos junto a nuestro bloque:

1import { InspectorControls } from '@wordpress/block-editor';
2import { Flex, FlexItem, SelectControl, Placeholder, PanelBody} from '@wordpress/components';
3

Y justo antes de nuestra vista previa agregamos nuestro control, así que modificamos el resultado de esta forma:

1return (
2 <>
3 <InspectorControls>
4 <PanelBody title={ __( 'Configuración', 'mah-users' ) }>
5 <SelectControl
6 label={ __( 'Selecciona un usuario', 'mah-users' ) }
7 value={ attributes.userId }
8 options={ users }
9 onChange={ ( userId ) => setAttributes( { userId: parseInt( userId, 10 ) } ) }
10 />
11 </PanelBody>
12 </InspectorControls>
13
14 <div { ...useBlockProps() }>
15 <Flex justify='start' align='start' gap="4">
16 <FlexItem>
17 <img src={ userAvatar } alt={ userName } />
18 </FlexItem>
19 <FlexItem isBlock>
20 <h3>{ userName }</h3>
21 <p>{ userDescription }</p>
22 </FlexItem>
23 </Flex>
24 </div>
25 </>
26);
27

Y ahora podremos ver que tenemos la opción de cambiar de usuario sin necesidad de eliminar nuestro bloque:

Controles
Nota

A grandes rasgos, InspectorControls es un portal que automáticamente coloca lo que pongamos dentro en el sidebar activo para el bloque que estamos editando, esto es útil ya que nos permite colocar todos los componentes de un bloque en el mismo lugar, en lugar de tener que modificar varios archivos relacionados.

PASO 4.4: OFRECER UNA FORMA DE ELIMINAR LA SELECCIÓN Y VOLVER AL DROPDOWN (OPCIONAL)

Este último paso es completamente opcional porque ya estamos ofreciendo una forma de cambiar de usuario, pero quería mostrar una forma de reiniciar el dropdown y poder volver a escoger un usuario.

La parte funcional es bastante simple de hecho, lo único que hay que hacer es asignar un valor nulo al atributo, y para ello añadiremos un botón justo a un lado de nuestros InspectorControls.

Primero importamos los componentes:

1import { InspectorControls, BlockControls } from '@wordpress/block-editor';
2import { Flex, FlexItem, SelectControl, Placeholder, PanelBody, ToolbarGroup, ToolbarButton } from '@wordpress/components';
3

Y simplemente agregamos el botón que importamos dentro del componente BlockControls:

1<InspectorControls>
2// …
3</InspectorControls>
4
5<BlockControls group="block">
6 <ToolbarGroup>
7 <ToolbarButton
8 icon={ closeSmall }
9 title={ __( 'Eliminar selección', 'mah-users' ) }
10 onClick={ () => setAttributes( { userId: null } ) }
11 />
12 </ToolbarGroup>
13</BlockControls>
14

Y sólo con eso, podremos ver un nuevo botón aparecer cuando seleccionamos el bloque, al presionarlo veremos como la selección es eliminada y regresamos al inicio:

Posición del botón
Nota

Al igual que InspectorControls, BlockControls funciona como un portal.

CONCLUSIÓN

Eso es todo por este tutorial (que como de costumbre se largó un poco más de lo esperado 😅️). Lo único que nos quedaría hacer sería pedir la información en PHP cuando mostremos el bloque en el frontend.

LEER MÁS