Envío de formularios sin cabeza con la API REST de WordPress

Si está creando un sitio de WordPress, necesita una buena razón para no elegir un complemento de formulario de WordPress. Son convenientes y ofrecen muchas personalizaciones que requerirían un gran esfuerzo para construirlas desde cero. Representan el HTML, validan los datos, almacenan los envíos y brindan integración con servicios de terceros.

Pero supongamos que planeamos utilizar WordPress como un CMS sin cabeza. En este caso, interactuaremos principalmente con la API REST (o GraphQL). La parte del front-end pasa a ser nuestra responsabilidad entera y ya no podemos depender de complementos de formulario para hacer el trabajo pesado en esa área. Ahora estamos en el asiento del conductor en lo que respeta al frontal.

Los formularios eran un problema resuelto, pero ahora tenemos que decidir qué hacer con ellos. Tenemos un par de opciones:

  • ¿Utilizamos nuestra propia API personalizada si tenemos tal cosa? Si no, y no queremos crear uno, podemos optar por un servicio. Hay muchos buenos proveedores de formularios estáticos y constantemente aparecen otros nuevos.
  • ¿Podemos seguir usando el complemento de WordPress que ya usamos y aprovechar su validación, almacenamiento e integración?

El complemento de formulario gratuito más popular, Contact Form 7, tiene un punto final de API REST de envío, al igual que el conocido complemento pago, Gravity Forms, entre otros.

Desde un punto de vista técnico, no existe una diferencia real entre enviar los datos del formulario a un punto final proporcionado por un servicio o un complemento de WordPress. Entonces, tenemos que decidir en base a diferentes criterios. El precio es obvio; Después de eso está la disponibilidad de la instalación de WordPress y su API REST. El envío a un punto final presupone que siempre esté disponible públicamente. Eso ya está claro cuando se trata de servicios porque pagamos para que estén disponibles. Algunos ajustes pueden limitar el acceso a WordPress únicamente a los procesos de edición y construcción. Otra cosa a considerar es dónde desea almacenar los datos, particularmente de una manera que cumpla con las regulaciones GPDR.

Cuando se trata de funciones más allá del envío, los complementos de formulario de WordPress son difíciles de igualar. Tienen su propio ecosistema, complementos capaces de generar informes, archivos PDF, integración fácilmente disponible con boletines y servicios de pago. Pocos servicios ofrecen tanto en un solo paquete.

Incluso si usamos WordPress de la manera “tradicional” con la interfaz basada en un tema de WordPress, usar la API REST de un complemento de formulario puede tener sentido en muchos casos. Por ejemplo, si estamos desarrollando un tema utilizando un marco CSS de utilidad, diseñe el formulario renderizado con marcado fijo estructurado con una convención de clase similar a BEM deja un sabor amargo en la boca de cualquier desarrollador.

El propósito de este artículo es presentar los dos puntos finales de envío de complementos de formularios de WordPress y mostrar una manera de recrear los comportamientos típicos relacionados con los formularios a los que estamos acostumbrados. A la hora de enviar un formulario, en general, tenemos que afrontar dos problemas principales. Uno es el envío de los datos en sí y el otro es proporcionar comentarios significativos al usuario.

Entonces, comencemos por ahí.

Los puntos finales

Enviar datos es la parte más sencilla. Ambos puntos finales esperan una POSTsolicitud y la parte dinámica de la URL es el ID del formulario.

La API REST del Formulario de contacto 7 está disponible inmediatamente cuando se activa el complemento y se ve así:

https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/FORM_ID/feedback

Si trabajamos con Gravity Forms, el punto final toma esta forma:

https://your-site.tld/wp-json/gf/v2/forms/FORM_ID/submissions

La API REST de Gravity Forms está deshabilitada de forma predeterminada. Para habilitarlo, tenemos que ir a la configuración del complemento, luego a la página de la API REST y marcar la opción “Habilitar acceso a la API”. No es necesario crear una clave API, ya que el punto final de envío del formulario no la requiere.

El cuerpo de la solicitud.

Nuestro formulario de ejemplo tiene cinco campos con las siguientes reglas:

  • un campo de texto requerido
  • un campo de correo electrónico obligatorio
  • un campo de fecha obligatoria que acepta fechas anteriores al 4 de octubre de 1957
  • un área de texto opcional
  • una casilla de verificación requerida

Para las claves de solicitud de Contact Form 7 body, tenemos que definirlas con la sintaxis de etiquetas de formulario:

{  "somebodys-name": "Marian Kenney",  "any-email": "marian2210@geocities.com",  "before-space-age": "1922-03-11",  "optional-message": "",  "fake-terms": "1"}

Gravity Forms espera las claves en un formato diferente. Tenemos que utilizar un ID de campo incremental generado automáticamente con el input_prefijo. La identificación es visible cuando estás editando el campo.

{  "input_1": "Marian Kenney",  "input_2": "marian2210@geocities.com",  "input_3": "1922-03-11",  "input_4": "",  "input_5_1": "1"}

Enviando los datos

Podemos ahorrarnos mucho trabajo si utilizamos las claves esperadas para los nameatributos de las entradas. De lo contrario, tenemos que asignar los nombres de entrada a las claves.

Al juntar todo, obtenemos una estructura HTML como esta para el Formulario de contacto 7:

form action="https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/FORM_ID/feedback" method="post"  label for="somebodys-name"Somebody's name/label  input type="text" name="somebodys-name"  !-- Other input elements --  button type="submit"Submit/button/form

En el caso de Gravity Forms, solo necesitamos cambiar los atributos actiony name:

form action="https://your-site.tld/wp-json/gf/v2/forms/FORM_ID/submissions" method="post"  label for="input_1"Somebody's name/label  input type="text" name="input_1"  !-- Other input elements --  button type="submit"Submit/button/form

Dado que toda la información requerida está disponible en HTML, estamos listos para enviar la solicitud. Una forma de hacer esto es usar FormDataen combinación con fetch:

const formSubmissionHandler = (event) = {  event.preventDefault();  const formElement = event.target,    { action, method } = formElement,    body = new FormData(formElement);  fetch(action, {    method,    body  })    .then((response) = response.json())    .then((response) = {      // Determine if the submission is not valid      if (isFormSubmissionError(response)) {        // Handle the case when there are validation errors      }      // Handle the happy path    })    .catch((error) = {      // Handle the case when there's a problem with the request    });};const formElement = document.querySelector("form");formElement.addEventListener("submit", formSubmissionHandler);

Podemos enviar el envío con poco esfuerzo, pero la experiencia del usuario es deficiente, por decir lo menos. Debemos a los usuarios toda la orientación posible para enviar el formulario con éxito. Como mínimo, eso significa que debemos:

  • mostrar un mensaje de error o éxito global,
  • agregar mensajes de error de validación de campos en línea y posibles direcciones, y
  • llamar la atención sobre las partes que requieren atención con clases especiales.

Validación de campo

Además de utilizar la validación de formularios HTML incorporada, podemos usar JavaScript para una validación adicional del lado del cliente y/o aprovechar la validación del lado del servidor.

Cuando se trata de validación del lado del servidor, tanto Contact Form 7 como Gravity Forms la ofrecen de forma inmediata y devuelven los mensajes de error de validación como parte de la respuesta. Esto es conveniente ya que podemos controlar las reglas de validación desde el administrador de WordPress.

Para reglas de validación más complejas, como la validación de campos condicionales, podría tener sentido confiar solo en el lado del servidor porque mantener la validación de JavaScript del front-end sincronizada con la configuración de los complementos puede convertirse en un problema de mantenimiento.

Si optamos únicamente por la validación del lado del servidor, la tarea consiste en analizar la respuesta, extraer los datos relevantes y manipular DOM, como insertar elementos y alternar nombres de clases.

Mensajes de respuesta

La respuesta cuando hay un error de validación para el Formulario de contacto 7 se ve así:

{  "into": "#",  "status": "validation_failed",  "message": "One or more fields have an error. Please check and try again.",  "posted_data_hash": "",  "invalid_fields": [    {      "into": "span.wpcf7-form-control-wrap.somebodys-name",      "message": "The field is required.",      "idref": null,      "error_id": "-ve-somebodys-name"    },    {      "into": "span.wpcf7-form-control-wrap.any-email",      "message": "The field is required.",      "idref": null,      "error_id": "-ve-any-email"    },    {      "into": "span.wpcf7-form-control-wrap.before-space-age",      "message": "The field is required.",      "idref": null,      "error_id": "-ve-before-space-age"    },    {      "into": "span.wpcf7-form-control-wrap.fake-terms",      "message": "You must accept the terms and conditions before sending your message.",      "idref": null,      "error_id": "-ve-fake-terms"    }  ]}

Si el envío es exitoso, la respuesta se verá así:

{  "into": "#",  "status": "mail_sent",  "message": "Thank you for your message. It has been sent.",  "posted_data_hash": "d52f9f9de995287195409fe6dcde0c50"}

En comparación con esto, la respuesta al error de validación de Gravity Forms es más compacta:

{  "is_valid": false,  "validation_messages": {    "1": "This field is required.",    "2": "This field is required.",    "3": "This field is required.",    "5": "This field is required."  },  "page_number": 1,  "source_page_number": 1}

Pero la respuesta ante un envío exitoso es mayor:

{  "is_valid": true,  "page_number": 0,  "source_page_number": 1,  "confirmation_message": "div id='gform_confirmation_wrapper_1' class='gform_confirmation_wrapper 'div id='gform_confirmation_message_1' class='gform_confirmation_message_1 gform_confirmation_message'Thanks for contacting us! We will get in touch with you shortly./div/div",  "confirmation_type": "message"}

Si bien ambos contienen la información que necesitamos, no siguen una convención común y ambos tienen sus peculiaridades. Por ejemplo, el mensaje de confirmación en Gravity Forms contiene HTML y las claves del mensaje de validación no tienen el input_prefijo, el prefijo que se requiere cuando enviamos la solicitud. Por otro lado, los errores de validación en Contact Form 7 contienen información que es relevante sólo para su implementación front-end. Las claves de campo no se pueden utilizar inmediatamente; hay que extraerlos.

En una situación como esta, en lugar de trabajar con la respuesta que obtenemos, es mejor idear un formato ideal y deseado. Una vez que tengamos eso, podremos encontrar formas de transformar la respuesta original a lo que consideremos adecuado. Si combinamos lo mejor de los dos escenarios y eliminamos las partes irrelevantes para nuestro caso de uso, terminaremos con algo como esto:

{  "isSuccess": false,  "message": "One or more fields have an error. Please check and try again.",  "validationError": {    "somebodys-name": "This field is required.",    "any-email": "This field is required.",    "input_3": "This field is required.",    "input_5": "This field is required."  }}

Y si el envío es exitoso, estableceríamos y isSuccessdevolveríamos trueun objeto de error de validación vacío:

{  "isSuccess": true,  "message": "Thanks for contacting us! We will get in touch with you shortly.",  "validationError": {}}

Ahora es cuestión de transformar lo que tenemos en lo que necesitamos. El código para normalizar la respuesta de Contact Forms 7 es este:

const normalizeContactForm7Response = (response) = {  // The other possible statuses are different kind of errors  const isSuccess = response.status === 'mail_sent';  // A message is provided for all statuses  const message = response.message;  const validationError = isSuccess    ? {}    : // We transform an array of objects into an object    Object.fromEntries(      response.invalid_fields.map((error) = {        // Extracts the part after "cf7-form-control-wrap"        const key = /cf7[-a-z]*.(.*)/.exec(error.into)[1];        return [key, error.message];      })    );  return {    isSuccess,    message,    validationError,  };};

El código para normalizar la respuesta de Gravity Forms termina siendo este:

const normalizeGravityFormsResponse = (response) = {  // Provided already as a boolean in the response  const isSuccess = response.is_valid;  const message = isSuccess    ? // Comes wrapped in a HTML and we likely don't need that      stripHtml(response.confirmation_message)    : // No general error message, so we set a fallback      'There was a problem with your submission.';  const validationError = isSuccess    ? {}    : // We replace the keys with the prefixed version;      // this way the request and response matches      Object.fromEntries(        Object.entries(            response.validation_messages        ).map(([key, value]) = [`input_${key}`, value])      );  return {    isSuccess,    message,    validationError,  };};

Todavía nos falta una forma de mostrar los errores de validación, los mensajes de éxito y la alternancia de clases. Sin embargo, tenemos una forma clara de acceder a los datos que necesitamos y eliminamos todas las inconsistencias en las respuestas con una ligera abstracción. Una vez armado, está listo para incluirse en una base de código existente, o podemos continuar construyendo sobre ella.

Hay muchas maneras de abordar la parte restante. Lo que tenga sentido dependerá del proyecto. Para situaciones en las que principalmente tenemos que reaccionar a cambios de estado, una biblioteca declarativa y reactiva puede ser de gran ayuda. Alpine.js se cubrió aquí en CSS-Tricks y es perfecto tanto para demostraciones como para usarlo en sitios de producción. Casi sin modificaciones, podemos reutilizar el código del ejemplo anterior. Sólo necesitamos agregar las directivas adecuadas y en los lugares correctos.

Terminando

Se puede igualar la experiencia de front-end que brindan los complementos de formularios de WordPress con relativa facilidad para obtener formularios sencillos y sin complicaciones, y de una manera que sea reutilizable de un proyecto a otro. Incluso podemos lograrlo de una manera que nos permita cambiar el complemento sin afectar la interfaz.

Claro, se necesita tiempo y esfuerzo para crear un formulario de varias páginas, vistas previas de las imágenes cargadas u otras funciones avanzadas que normalmente incluiríamos directamente en un complemento, pero cuanto más exclusivos sean los requisitos que debemos cumplir, más Tiene sentido usar el punto final de envío ya que no tenemos que trabajar contra la implementación de front-end dada que intenta resolver muchos problemas, pero nunca el que queremos en particular.

El uso de WordPress como un CMS sin cabeza para acceder a la API REST de un complemento de formulario para acceder a los puntos finales de envío seguramente se convertirá en una práctica más utilizada. Es algo que vale la pena explorar y tener en cuenta. En el futuro, no me sorprendería ver complementos de formularios de WordPress diseñados principalmente para funcionar en un contexto sin cabeza como este. Me imagino un complemento donde la renderización frontal es una característica adicional que no es una parte integral de su núcleo. Queda por explorar qué consecuencias tendría eso y si podría tener éxito comercial, pero es un espacio fascinante para observar la evolución.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir