Contents
Introducción
Cuando desarrollas bloques para el editor de WordPress (Gutenberg), es habitual que con el tiempo cambies la estructura de los atributos, el marcado HTML de salida o la manera de renderizar el bloque. Si no proporcionas una estrategia de migración, los bloques antiguos en entradas existentes pueden dejar de mostrarse correctamente o perder datos de los usuarios. Las deprecations (entradas en la propiedad deprecated al registrar un bloque) son la herramienta recomendada para describir versiones antiguas del bloque y transformar sus atributos al formato actual de forma automática y segura.
Conceptos clave
- save() deprecado: recrea exactamente el HTML que las versiones antiguas guardaban. Sirve para que el parser de bloques pueda reconocer ese marcado histórico.
- attributes en la entrada deprecated: descríbela tal como existía antes — tipos, fuentes (source), selectores, defaults — para que WordPress pueda extraer los valores desde el HTML histórico.
- migrate(): función que toma los atributos extraídos del antiguo HTML y devuelve un objeto con los atributos en la forma esperada por la versión actual del bloque.
- orden de comprobación: al parsear una entrada, WordPress intentará casar el HTML con la versión actual del bloque. Si no hay coincidencia, probará cada entrada de deprecated en orden (del más reciente al más antiguo).
Por qué usar deprecations en vez de rehacer todo manualmente
- Mantienes compatibilidad hacia atrás y los usuarios no pierden contenido.
- Puedes transformar datos de forma programada y predecible con migrate().
- Evitas tener que hacer conversiones masivas en la base de datos fuera del flujo de edición.
Patrones comunes de migración
- Renombrar atributos: mover un valor de un nombre antiguo a uno nuevo.
- Cambiar tipo: convertir un booleano a un string (por ejemplo, openInNewTab -> linkTarget).
- Unir atributos: combinar dos atributos antiguos en uno nuevo (p. ej. margenX margenY -> margin).
- Dividir atributos: separar un atributo en varios.
- Pasar de HTML marcado a innerBlocks: cuando antes la información estaba en HTML y ahora es un bloque anidado.
- Normalizar formatos (colores, tamaños): pasar de valores simples a objetos con slug, color, etc.
Flujo recomendado para introducir una migración
- Identifica exactamente el marcado que las versiones antiguas guardaban en la entrada (ej.: HTML producido por la función save anterior).
- Define en la entrada deprecated: los atributos antiguos (incluyendo source/selector) y una save que reproduzca exactamente el HTML histórico.
- Implementa migrate(attributes) para convertir los datos antiguos en los nuevos atributos.
- Prueba con documentos que contienen bloques antiguos: al abrir en el editor, WordPress debe reconocer la versión deprecada, ejecutar la migración y actualizar el bloque al formato actual.
- Repite el proceso por cada versión anterior que quieras soportar (ordenadas de la más reciente a la más antigua).
Ejemplo práctico 1 — Renombrar y cambiar tipo
Supongamos que la versión 1 del bloque tenía estos atributos:
- ctaText (string)
- openInNewTab (boolean)
La versión 2 renombra y unifica esos atributos a:
- buttonText (string)
- linkTarget (string) con o _blank
Registrar el bloque con una entrada deprecada que reconozca el marcado antiguo y lo migre:
// Ejemplo: registerBlockType con deprecated const { registerBlockType } = wp.blocks const { RichText } = wp.blockEditor registerBlockType(mi-plugin/cta, { title: CTA, attributes: { buttonText: { type: string, source: html, selector: a }, linkTarget: { type: string, default: } }, edit: (props) =gt { // ... implementación del editor actual ... }, save: (props) =gt { const { buttonText, linkTarget } = props.attributes return ( lta className=mi-cta target={ linkTarget undefined }gt ltRichText.Content value={ buttonText } /gt lt/agt ) }, deprecated: [ { // Atributos de la versión 1 attributes: { ctaText: { type: string, source: html, selector: a }, openInNewTab: { type: boolean, default: false } }, // Save que reproduce el HTML que existía en la V1 save: (props) =gt { const { ctaText, openInNewTab } = props.attributes return ( lta className=mi-cta target={ openInNewTab ? _blank : undefined }gt ltRichText.Content value={ ctaText } /gt lt/agt ) }, // Migración de atributos V1 -> V2 migrate: (attributes) =gt { const { ctaText, openInNewTab } = attributes return { buttonText: ctaText, linkTarget: openInNewTab ? _blank : } } } ] })
Cómo funciona
Si el post contiene el HTML antiguo generado por la V1, el parser lo reconocerá usando la save definida en la entrada deprecated. WordPress extraerá los atributos según la definición de attributes en esa deprecated, ejecutará migrate para obtener los atributos actuales y reescribirá el bloque con el formato de la versión actual.
Ejemplo 2 — Cambio de color simple a objeto color
Antes el color de fondo era un string simple: red o #ff0000. Ahora queremos un objeto que contenga slug y color. La migración debe convertir la cadena al nuevo objeto.
// Deprecated para convertir color string a objeto { slug, color } deprecated: [ { attributes: { bgColor: { type: string } // ej. red o #ff0000 }, save: (props) =gt { const { bgColor } = props.attributes return ltdiv className={ mi-block bg-{ bgColor } } style={ { backgroundColor: bgColor } }gt...lt/divgt }, migrate: (attributes) =gt { const { bgColor } = attributes // Lógica simple para intentar resolver slug y color hex const isHex = /^#([0-9A-F]{3}){1,2}/i.test(bgColor ) if (isHex) { return { background: { slug: , color: bgColor } } } // si es un nombre, usamos slug y vaciamos color return { background: { slug: bgColor, color: } } } } ]
Ejemplo 3 — De HTML serializado a innerBlocks (patrón común)
Si tu bloque antes guardaba todo como HTML (p. ej. un quote con ltblockquotegt y ltcitegt) y ahora quieres usar innerBlocks o RichText con estructura diferente, debes:
- Definir la deprecated con attributes que extraigan el contenido antiguo (source: html o text con el selector correcto).
- En migrate, convertir esa cadena HTML en atributos actuales. Si transformas a innerBlocks, podrías convertir el HTML a bloques y devolver atributos compatibles con el nuevo formato en algunos casos es necesario implementar una transform para convertir a innerBlocks (ver notas).
// Ejemplo simplificado: extraemos contenido previo y lo convertimos a nuevo atributo content deprecated: [ { attributes: { quoteHtml: { type: string, source: html, selector: .mi-quote } }, save: (props) =gt { const { quoteHtml } = props.attributes return ltdiv className=mi-quotegt ltdiv dangerouslySetInnerHTML={ { __html: quoteHtml } } /gt lt/divgt }, migrate: (attributes) =gt { // Aquí transformamos el HTML antiguo a lo que necesitemos (p. ej. plain text para RichText) const { quoteHtml } = attributes const plain = stripTags(quoteHtml) // función de ejemplo para limpiar HTML return { content: plain } } } ]
Validación y pruebas
- Crea un post de prueba que contenga el bloque en su versión antigua (puedes copiar el HTML guardado en la DB o usar una entrada de contenido antiguo).
- Abre el editor: si las deprecated se han definido correctamente, WordPress reconocerá que el bloque corresponde a una versión antigua y aplicará la migración (si el migrate está implementado).
- Comprueba que la entrada se guarda con la nueva estructura (la migración reescribe el contenido en el post).
- Utiliza diferentes combinaciones de atributos para verificar bordes: valores nulos, strings vacíos, formatos distintos (hex vs nombre de color), etc.
- Revisa el marcado final en la base de datos (tabla wp_posts) para verificar que no hay pérdida de datos.
Buenas prácticas
- Documenta cada entrada deprecated con un comentario que explique qué versión corresponde y por qué se realizó la migración.
- Escribe migraciones idempotentes: si se ejecutan varias veces deberían dejar los datos en el mismo estado.
- Prefiere migraciones en el cliente (deprecated migrate) para que la conversión ocurra cuando el usuario edite el post, evitando operaciones masivas en la DB que puedan ser peligrosas.
- Evita lógica pesada en migrate si la conversión es compleja considera escribir tests y/o herramientas offline de migración si fuese imprescindible hacerlo a gran escala.
- Siempre que el antiguo save use HTML dinámico, asegúrate de reproducirlo con exactitud para que el parser lo identifique correctamente.
- Si cambias JSON de block.json con atributos, recuerda que block.json no soporta la propiedad deprecated la definición de deprecated se hace en el script JS cuando llamas a registerBlockType o usando wp.blocks.registerBlockType en tiempo de ejecución.
Tabla rápida: patrones y soluciones
Problema | Estrategia con deprecated |
---|---|
Renombre de atributo | Definir atributo antiguo en deprecated migrate que copie al nuevo nombre. |
Cambio de tipo (booleano → string) | En migrate convierte el valor booleano a la representación string esperada. |
Nuevo objeto color | Convierta el string en objeto en migrate (intentar detectar hex vs slug). |
HTML antiguo → innerBlocks | Extraer HTML en deprecated.attributes y convertir en migrate si es necesario, proveer una transform que convierta programáticamente a innerBlocks. |
Consejos adicionales y errores comunes
- No olvides incluir la save antigua en la entrada deprecated: sin esa función WordPress no podrá reconocer el HTML histórico correctamente.
- Si el antiguo marcado dependía de clases dinámicas o atributos data-, reproducelos exactamente en la save deprecated.
- Si no implementas migrate pero defines deprecated, WordPress podrá editar el bloque en su versión antigua (mostrando el bloque como versión deprecated en el editor) — pero no habrá conversión automática al formato actual.
- Ten en cuenta la seguridad al manipular HTML: si usas dangerouslySetInnerHTML o transforms que parseen HTML, sanea adecuadamente si procede.
Ejemplo final: bloque completo con dos deprecated (dos versiones antiguas)
// Registro completo simplificado con dos deprecated (V2 y V1) registerBlockType(mi-plugin/complex, { title: Bloque Complex, attributes: { // atributos actuales title: { type: string, source: html, selector: h2 }, content: { type: string, source: html, selector: .content }, style: { type: object, default: { bg: { slug: , color: } } } }, edit: () =gt {/ ... /}, save: (props) =gt { const { title, content, style } = props.attributes return ( ltdiv className=complex-block style={ { backgroundColor: style.bg.color undefined } }gt lth2gtltRichText.Content value={ title } /gtlt/h2gt ltdiv className=contentgtltRichText.Content value={ content } /gtlt/divgt lt/divgt ) }, deprecated: [ // V2: tenía bgColor simple { attributes: { titleV2: { type: string, source: html, selector: h2 }, contentV2: { type: string, source: html, selector: .content }, bgColor: { type: string } }, save: (props) =gt { const { titleV2, contentV2, bgColor } = props.attributes return ( ltdiv className={ complex-block bg-{ bgColor } } style={ { backgroundColor: bgColor } }gt lth2gtltRichText.Content value={ titleV2 } /gtlt/h2gt ltdiv className=contentgtltRichText.Content value={ contentV2 } /gtlt/divgt lt/divgt ) }, migrate: (attributes) =gt { const { titleV2, contentV2, bgColor } = attributes const isHex = /^#([0-9A-F]{3}){1,2}/i.test(bgColor ) return { title: titleV2, content: contentV2, style: { bg: { slug: isHex ? : bgColor, color: isHex ? bgColor : } } } } }, // V1: estructura aún más diferente { attributes: { rawHtml: { type: string, source: html } }, save: (props) =gt { return ltdiv className=complex-blockgt ltdiv dangerouslySetInnerHTML={ { __html: props.attributes.rawHtml } } /gt lt/divgt }, migrate: (attributes) =gt { // Conversión heurística: parsear rawHtml y extraer partes const raw = attributes.rawHtml const parsedTitle = extractBetween(raw, lth2gt, lt/h2gt) // función de ejemplo const parsedContent = extractBetween(raw, ltdiv class=contentgt, lt/divgt) return { title: parsedTitle , content: parsedContent , style: { bg: { slug: , color: } } } } } ] })
Conclusión
Las deprecations son la forma segura y recomendada de mantener compatibilidad hacia atrás para bloques de WordPress. Definir correctamente la save antigua, los attributes históricos y una función migrate robusta permite que los bloques antiguos se reconozcan y se actualicen automáticamente al abrir el editor. Con buenas pruebas y migraciones idempotentes, podrás evolucionar tus bloques sin forzar a los usuarios a revisiones manuales ni perder datos.
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |