Inicio / Blog / Cómo solucionar que acf_form() no guarde campos añadidos manualmente

Cómo solucionar que acf_form() no guarde campos añadidos manualmente

Luis Ruiz

Escrito por Luis Ruiz el

Si después de actualizar ACF algunos campos de tus formularios frontend han dejado de guardarse sin mostrar ningún error, probablemente no sea un problema de tu código. En este artículo veremos qué ha cambiado en acf_form(), por qué ocurre este comportamiento y qué solución conviene aplicar según cómo esté construido el formulario.

Ilustración de un formulario creado con acf_form() donde un campo añadido manualmente no se guarda tras enviar el formulario, mostrando la solución para guardar campos personalizados en ACF.

Actualizas ACF, pruebas un formulario frontend y todo parece funcionar. El contenido se crea, la redirección se ejecuta y no aparece ningún error. Sin embargo, algunos campos añadidos manualmente dejan de guardarse.

Si utilizas acf_form() y construyes parte del formulario con HTML personalizado, este comportamiento tiene una explicación. En este artículo veremos por qué ocurre, qué cambió en ACF y qué soluciones existen según cómo esté construido tu formulario.

Este artículo se centra en un cambio concreto relacionado con el guardado de campos. Si quieres aprender a crear formularios frontend con Advanced Custom Fields desde cero o entender mejor cómo funciona acf_form(), puedes consultar primero nuestra guía sobre formularios frontend con ACF.

Qué ha cambiado en acf_form()

El origen de este comportamiento está en un cambio introducido en Advanced Custom Fields 6.8.2, publicado el 26 de mayo de 2026. A partir de esta versión, acf_form() ya no procesa cualquier valor recibido dentro de $_POST['acf'], sino únicamente aquellos campos que forman parte del formulario.

En la práctica, esto significa que ACF solo guarda los campos que cumplen alguna de estas condiciones:

  • Están incluidos en el parámetro fields.
  • Pertenecen a alguno de los field_groups asignados al formulario.
  • Se incluyen mediante las reglas de ubicación del grupo de campos correspondiente.

Cualquier otro valor enviado con un nombre como:

$_POST['acf']['field_123456789abcd']

puede descartarse durante el proceso de guardado, aunque el formulario se haya enviado correctamente.

Antes de este cambio era relativamente habitual aprovechar un comportamiento más permisivo de acf_form(). Muchos desarrolladores renderizaban parte del formulario manualmente y utilizaban nombres con el formato acf[field_xxxxx], confiando en que ACF procesaría esos valores igual que si hubiera generado el campo de forma nativa.

Ese comportamiento ya no debe darse por hecho. El objetivo de este cambio es reforzar la seguridad de los formularios frontend, evitando que puedan modificarse campos que realmente no forman parte del formulario definido por ACF.

Desde el punto de vista de la seguridad, la decisión tiene todo el sentido. Sin embargo, también implica que formularios que llevaban años funcionando pueden dejar de guardar algunos valores sin mostrar ningún error visible, lo que hace que el problema resulte especialmente difícil de detectar si no conoces este cambio.

Qué formularios pueden verse afectados

No todos los formularios creados con acf_form() se ven afectados por este cambio. Si dejas que ACF renderice todos los campos y no modificas su estructura, lo más probable es que todo siga funcionando exactamente igual.

El problema suele aparecer cuando el formulario combina campos generados por ACF con HTML personalizado. Es un enfoque bastante habitual cuando necesitas crear una interfaz más flexible o adaptar la experiencia de usuario a las necesidades del proyecto.

Un caso muy común sería el siguiente:

  • Renderizas un formulario con acf_form().
  • Limitas los campos visibles mediante el parámetro fields.
  • Añades otros controles utilizando html_before_fields, html_after_fields o directamente desde una plantilla personalizada.
  • Esos controles utilizan nombres como acf[field_xxxxxxxxxxxxx] para aprovechar el sistema de guardado de ACF.

Por ejemplo:

acf_form(
    array(
        'post_id'           => 'new_post',
        'post_title'        => false,
        'post_content'      => false,
        'fields'            => array(
            'field_nombre',
            'field_email',
            'field_mensaje',
        ),
        'html_after_fields' => $html_checkboxes_restaurantes,
    )
);

Supongamos que el contenido de $html_checkboxes_restaurantes genera un campo como este:

<input type="checkbox" name="acf[field_restaurantes][]" value="Pepiño">

Hasta ahora podía parecer suficiente para que ACF guardara automáticamente el valor enviado. Sin embargo, si field_restaurantes no forma parte del formulario según la configuración de ACF, ese campo puede dejar de procesarse durante el guardado.

Es importante entender que el problema no está en el HTML ni en el tipo de campo que estés utilizando. Puedes generar checkboxes, selects, campos ocultos o cualquier otro control válido. Lo que determina si ACF guardará ese valor es si reconoce esa field key como parte del formulario.

Por eso, este cambio suele afectar especialmente a proyectos que llevan tiempo en producción y que, por necesidades de diseño o de lógica de negocio, renderizan algunos campos manualmente mientras delegan el resto del formulario en acf_form(). Es una técnica completamente válida, pero ahora requiere indicar explícitamente a ACF qué campos debe aceptar durante el proceso de guardado.

Cómo comprobar si este cambio te está afectando

Si después de actualizar ACF algunos campos dejan de guardarse sin mostrar ningún error, merece la pena revisar cómo está construido el formulario. En la mayoría de los casos, el problema aparece cuando acf_form() solo renderiza parte de los campos y el resto se generan manualmente.

Una forma rápida de identificar si tu proyecto puede verse afectado es comprobar si cumple alguna de estas condiciones:

  • Utiliza acf_form() para crear formularios frontend.
  • Limita los campos mediante el parámetro fields.
  • Añade HTML personalizado con html_before_fields, html_after_fields, hooks o plantillas propias.
  • Genera manualmente campos con nombres como acf[field_xxxxx].
  • Depende de campos que no aparecen en fields ni pertenecen a un field_group asociado al formulario.

También puede ser útil buscar en el proyecto algunos patrones de código como estos:

acf_form(
   'fields'
   'field_groups'
   'html_before_fields'
   'html_after_fields'
   name="acf[
      update_field(
         ....

Si encuentras alguno de ellos, el siguiente paso es probar el flujo completo del formulario:

  1. Crea o edita un contenido desde el formulario frontend.
  2. Rellena también los campos que se generan manualmente.
  3. Envía el formulario.
  4. Comprueba desde el administrador si esos valores se han guardado.
  5. Revisa además cualquier funcionalidad que dependa de esos campos, como filtros, columnas personalizadas, envíos de correo o automatizaciones.

Si el contenido principal se guarda correctamente, pero solo fallan los campos añadidos manualmente, es muy probable que el origen del problema sea este cambio en el funcionamiento de acf_form().

Antes de modificar el código, también conviene revisar la consola del navegador y el registro de errores de PHP. Aunque este comportamiento normalmente no genera errores visibles, hacerlo te permitirá descartar problemas de JavaScript, conflictos con otros plugins o errores del servidor que puedan estar provocando un síntoma parecido.

En Bubuku solemos empezar siempre por reproducir el problema antes de aplicar ninguna solución. En este tipo de incidencias es fácil asumir que el fallo está en el código que genera el HTML, cuando en realidad el formulario se está enviando correctamente y es ACF quien decide ignorar determinados campos durante el guardado.

Solución 1: incluir el campo en fields o field_groups

Siempre que sea posible, esta es la solución más sencilla y recomendable. Si el campo forma parte del formulario, lo ideal es que ACF también lo considere parte de su configuración, en lugar de confiar únicamente en el HTML generado manualmente.

La forma más directa de conseguirlo es añadir la field key al parámetro fields:

acf_form(
    array(
        'post_id'      => 'new_post',
        'post_title'   => false,
        'post_content' => false,
        'fields'       => array(
            'field_nombre',
            'field_email',
            'field_mensaje',
            'field_restaurantes',
        ),
    )
);

Otra posibilidad consiste en renderizar directamente el grupo de campos completo utilizando field_groups:

acf_form(
    array(
        'post_id'      => 'new_post',
        'post_title'   => false,
        'post_content' => false,
        'field_groups' => array(
            Pepiño,
            Manolito
        ),
    )
);

Con cualquiera de estas opciones, el comportamiento vuelve a ser el esperado porque ACF conoce de antemano qué campos forman parte del formulario. Esto le permite renderizarlos, validarlos y guardarlos utilizando su flujo habitual, sin necesidad de realizar ninguna lógica adicional.

Sin embargo, esta solución no siempre encaja en todos los proyectos. En muchos desarrollos el HTML se genera manualmente porque el campo necesita una interfaz completamente personalizada: listas filtradas según el usuario conectado, agrupaciones especiales, buscadores dinámicos o componentes que no coinciden con la representación estándar de ACF.

En esos casos, incluir el campo en fields únicamente para que ACF lo procese puede generar un efecto secundario poco deseable: el mismo campo termina renderizándose dos veces, una por ACF y otra por tu propio código. Aunque ocultar el campo nativo con CSS puede servir como solución rápida, no suele ser la alternativa más limpia ni la más fácil de mantener.

En proyectos como los que desarrollamos en Bubuku, intentamos que cada campo tenga un único responsable de renderizar la interfaz. Si el HTML necesita ser completamente personalizado, preferimos que ACF se encargue únicamente de la validación y el guardado, dejando toda la presentación en manos del código del proyecto. Ese enfoque suele simplificar el mantenimiento y evita comportamientos difíciles de depurar cuando el formulario evoluciona.

Solución 2: usar acf/form/allowed_field_keys

Cuando el campo ya existe en ACF, pero necesitas renderizar su HTML manualmente, la solución más mantenible es utilizar el filtro acf/form/allowed_field_keys.

Este filtro, incorporado junto con los cambios de seguridad en los formularios frontend, permite ampliar la lista de field keys que ACF considera válidas durante el guardado. En otras palabras, puedes seguir construyendo una interfaz completamente personalizada sin renunciar al sistema de validación y guardado del propio plugin.

Esta diferencia es importante porque no estás sustituyendo el comportamiento de ACF, sino ampliándolo de forma controlada. ACF sigue siendo el encargado de validar el campo, aplicar sus filtros y almacenarlo igual que cualquier otro campo del formulario.

add_filter( 'acf/form/allowed_field_keys', 'prefix_form_allow_restaurant_key', 10, 2 );
/**
 * Permite guardar el campo de restaurantes cuando se renderiza manualmente
 * dentro de determinados formularios frontend.
 *
 * @param array $keys Keys ACF permitidas en $_POST['acf'].
 * @param array $form Configuración validada del formulario.
 * @return array
 */
function prefix_form_allow_restaurant_key( $keys, $form ) {
    if ( is_admin() ) {
        return $keys;
    }
    $restaurant_field_key = 'field_restaurantes';
    $allowed_post_types = array(
        'post',
        'order_custom'
    );
    $post_type = '';
    if ( isset( $form['post_id'] ) && 'new_post' === $form['post_id'] ) {
        $post_type = $form['new_post']['post_type'] ?? '';
    } elseif ( ! empty( $form['post_id'] ) && is_numeric( $form['post_id'] ) ) {
        $post_type = get_post_type( absint( $form['post_id'] ) );
    }
    if ( ! in_array( $post_type, $allowed_post_types, true ) ) {
        return $keys;
    }
    $keys[] = $restaurant_field_key;
    return array_values( array_unique( $keys ) );
}

En este ejemplo, field_restaurantes se añade a la lista de campos que ACF acepta durante el guardado, pero únicamente para los tipos de contenido esperados. En un proyecto real deberás sustituir este valor por la field key correspondiente, normalmente con un formato similar a field_abc123....

La ventaja de este enfoque es que el formulario continúa utilizando el flujo normal de ACF. No necesitas llamar a update_field(), ni volver a implementar validaciones, ni preocuparte de mantener dos procesos de guardado diferentes.

Esta solución resulta especialmente recomendable cuando:

  • El campo existe en ACF.
  • Necesitas personalizar completamente la interfaz del formulario.
  • El HTML utiliza correctamente nombres como acf[field_xxxxx].
  • Quieres que ACF siga encargándose de validar y guardar el valor.

En Bubuku solemos optar por este enfoque cuando el formulario requiere una interfaz completamente personalizada. Mantener la lógica de negocio dentro de ACF y limitar el código propio al renderizado del HTML hace que el proyecto sea más fácil de mantener, más escalable y mucho más resistente a futuras actualizaciones. Cuanto menos repliquemos el funcionamiento interno del plugin, menos trabajo tendremos cuando ACF evolucione.

Solución 3: guardar el campo manualmente con acf/save_post

Aunque acf/save_post sigue siendo una herramienta muy útil, ya no debería ser la primera opción si el único objetivo es conseguir que ACF vuelva a guardar un campo existente.

En ese escenario, acf/form/allowed_field_keys ofrece una solución más limpia porque permite que ACF continúe gestionando la validación y el guardado del campo como parte de su flujo habitual.

Sin embargo, acf/save_post sigue teniendo mucho sentido cuando necesitas ir un paso más allá. Por ejemplo, si el valor recibido no corresponde directamente a un campo ACF, necesitas transformar los datos antes de guardarlos o quieres almacenar la información en otro lugar distinto del campo original.

En estos casos, el flujo habitual consiste en:

  • Dejar que ACF guarde sus propios campos.
  • Ejecutar una acción sobre acf/save_post con una prioridad posterior.
  • Leer el valor recibido desde el formulario.
  • Validarlo y sanearlo.
  • Guardarlo utilizando update_field() u otra función adecuada.

Por ejemplo:

add_action( 'acf/save_post', 'prefix_save_manual_restaurants_field', 20 );
/**
 * Guarda manualmente un campo renderizado fuera de acf_form().
 *
 * @param int|string $post_id ID del contenido guardado.
 * @return void
 */
function prefix_save_manual_restaurants_field( $post_id ) {
    if ( empty( $_POST['acf']['field_restaurantes'] ) ) {
        return;
    }
    $restaurants = array_map(
        'absint',
        (array) $_POST['acf']['field_restaurantes']
    );
    update_field(
        'field_restaurantes',
        $restaurants,
        $post_id
    );
}

Nuestra recomendación es reservar acf/save_post para cuando realmente aporta valor. Si el campo ya existe en ACF y solo necesitas renderizar una interfaz personalizada, dejar que el propio plugin siga gestionando la validación y el guardado suele ser una solución más sencilla, escalable y fácil de mantener.

¿Qué solución conviene en cada caso?

Aunque las tres soluciones resuelven el mismo síntoma, no todas responden al mismo problema. Elegir una u otra dependerá de cómo esté construido tu formulario y del grado de personalización que necesites.

SituaciónSolución recomendada
El campo puede formar parte del formulario sin modificaciones.Añadirlo a fields o field_groups.
El campo existe en ACF, pero necesitas renderizar una interfaz completamente personalizada.Utilizar acf/form/allowed_field_keys.
Necesitas transformar los datos antes de guardarlos o almacenarlos en otro lugar.Gestionar el guardado mediante acf/save_post.

Como ocurre con muchas actualizaciones relacionadas con la seguridad, el objetivo de ACF no es romper la compatibilidad, sino evitar que un formulario frontend pueda modificar campos que no le corresponden. El cambio puede sorprender la primera vez que te encuentras con él, pero una vez entiendes cómo determina ACF qué campos pertenecen al formulario, resulta mucho más sencillo elegir la solución adecuada.

Al final, la idea es trabajar con el flujo de ACF en lugar de reemplazarlo. Cuanto menos código tengas que escribir para replicar su comportamiento interno, más sencillo será mantener el proyecto y adaptarlo a futuras actualizaciones del plugin.

Si tu formulario dejó de guardar algunos campos tras actualizar ACF, antes de reescribir la lógica de guardado merece la pena revisar si acf/form/allowed_field_keys resuelve el problema. En muchos casos, es la forma más sencilla de mantener una interfaz personalizada sin renunciar al flujo nativo de ACF.

Referencias y recursos para profundizar

Si quieres conocer con más detalle el cambio introducido en los formularios frontend de ACF o consultar la documentación oficial sobre el filtro utilizado en este artículo, estos recursos son un buen punto de partida:

Si necesitas desarrollar formularios frontend personalizados con Advanced Custom Fields o adaptar proyectos WordPress a cambios como este, en Bubuku podemos ayudarte a implementar soluciones robustas, escalables y fáciles de mantener.

Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.

Cookies estrictamente necesarias

Las cookies estrictamente necesarias tiene que activarse siempre para que podamos guardar tus preferencias de ajustes de cookies.

Cookies de terceros

Esta web utiliza Google Analytics para recopilar información anónima tal como el número de visitantes del sitio, o las páginas más populares.

Dejar esta cookie activa nos permite mejorar nuestra web.