{"id":233468,"date":"2023-02-15T17:01:00","date_gmt":"2023-02-15T14:01:00","guid":{"rendered":"https:\/\/wordpress.mediadoma.com\/?p=233468"},"modified":"2022-11-10T23:50:54","modified_gmt":"2022-11-10T20:50:54","slug":"tutorial-cree-un-tipo-de-campo-de-formularios-de-gravedad-personalizados-avanzados-y-como-manejar-multiples-valores-de-entrada","status":"publish","type":"post","link":"https:\/\/wordpress.mediadoma.com\/es\/tutorial-cree-un-tipo-de-campo-de-formularios-de-gravedad-personalizados-avanzados-y-como-manejar-multiples-valores-de-entrada\/","title":{"rendered":"Tutorial: Cree un tipo de campo de formularios de gravedad personalizados avanzados y c\u00f3mo manejar m\u00faltiples valores de entrada"},"content":{"rendered":"\n<p>En este tutorial, le mostrar\u00e9 c\u00f3mo crear un tipo de campo Gravity Forms personalizado avanzado. El campo tendr\u00e1 m\u00faltiples entradas y necesitar\u00e1 un manejo especial para almacenar y mostrar los valores enviados.<\/p>\n<h2>que haremos<\/h2>\n<p>En este ejemplo, asumo un ejemplo del propietario de un sitio web de WordPress que se ocupa de las entregas de almuerzos en un lugar de trabajo. El propietario tiene un formulario para que las personas llenen qu\u00e9 tipo de almuerzo quieren y cu\u00e1ntos para cada d\u00eda de la semana. Esto se puede resolver como un m\u00e9todo similar a una tabla para ingresar un n\u00famero para cualquier curso en cualquier d\u00eda que deseen la entrega.<\/p>\n<p><a href=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509da5343d.png\" data-rel=\"lightbox\" ><img decoding=\"async\" class=\"SDStudio-light-box-enable SDStudio-editor-tools-md-imp\" src=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509da5343d.png\" alt=\"Tutorial: Cree un tipo de campo de formularios de gravedad personalizados avanzados y c\u00f3mo manejar m\u00faltiples valores de entrada\" ><\/a><\/p>\n<p>Los cursos se pueden editar en la configuraci\u00f3n del campo en el editor de formularios y se pueden cambiar en cualquier momento. Y para cada env\u00edo de formulario, el propietario del sitio web obtiene una descripci\u00f3n completa de los valores enviados:<\/p>\n<p><a href=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509dbe8f8c.png\" data-rel=\"lightbox\"><img decoding=\"async\" class=\"SDStudio-light-box-enable SDStudio-editor-tools-md-imp\" src=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509dbe8f8c.png\" alt=\"Tutorial: Cree un tipo de campo de formularios de gravedad personalizados avanzados y c\u00f3mo manejar m\u00faltiples valores de entrada\" ><\/a><\/p>\n<p>Obviamente, esto es solo un ejemplo y probablemente necesite ajustarlo a su caso. Pero con este caso de ejemplo, tenemos la oportunidad de aprender a manejar m\u00faltiples entradas en un solo campo. Deber\u00eda darle algunas ideas sobre c\u00f3mo manejar su propio tipo de campo personalizado.<\/p>\n<h2>Antes de empezar a codificar<\/h2>\n<p>Antes de comenzar, necesitamos un lugar para agregar nuestro c\u00f3digo. Puede agregar esto en su tema <code>functions.php<\/code>o en su archivo de complemento.<\/p>\n<p>El m\u00e9todo que he elegido est\u00e1 orientado a objetos, lo que significa crear una clase que ampl\u00ede la <code>GF_Field<\/code>clase de Gravity Forms. Recomiendo poner la clase en un archivo separado en su proyecto. Tambi\u00e9n debe verificar que exista el complemento Gravity Forms antes de incluir su clase para evitar que su sitio se bloquee.<\/p>\n<p>Si est\u00e1 interesado, puede consultar <a href=\"https:\/\/docs.gravityforms.com\/gf_field\/\" target=\"_blank\" rel=\"noopener nofollow\" class=\"external external_icon\">la documentaci\u00f3n de Gravity Forms en GF_Field<\/a>. Encontrar\u00e1 m\u00e1s funciones y variables que podr\u00eda necesitar para su tipo de campo.<\/p>\n<p>Al extender la <code>GF_Field<\/code>clase, podemos simplemente elegir anular las funciones que necesitamos cambiar. En cuanto a las funciones que no anulamos, Gravity Forms ejecutar\u00e1 el valor predeterminado definido dentro de <code>GF_Field<\/code>. En el tutorial a continuaci\u00f3n, revisaremos cada funci\u00f3n que necesitamos anular para nuestro campo personalizado una por una. Sin m\u00e1s pre\u00e1mbulos, \u00a1comencemos!<\/p>\n<h2>Crear un tipo de campo personalizado<\/h2>\n<p>El primer paso es definir una clase PHP personalizada que ampl\u00ede <code>GF_Field<\/code>. Asigne a la clase un nombre \u00fanico y aseg\u00farese de que est\u00e9 incluido en su proyecto. Despu\u00e9s de la definici\u00f3n de la clase, ejecutamos la <code>register()<\/code>funci\u00f3n est\u00e1tica al <code>GF_Field<\/code>pasar una instancia de nuestra clase como par\u00e1metro. Esto inicializa nuestra clase y registra el tipo de campo.<\/p>\n<p>La \u00fanica variable requerida que necesita dentro de su clase es <code>$type<\/code>. La variable de clase <code>$type<\/code>debe ser \u00fanica y es un nombre de slug de su tipo de campo. En mi ejemplo lo he llamado &#8216; <code>food_delivery<\/code>&#8216;.<\/p>\n<pre><code>if (class_exists('GF_Field')) {\n    class FoodDelivery extends GF_Field {\n        public $type = 'food_delivery';\n\u00a0\n        \/\/ The rest of the code is added here...\n    }\n    GF_Fields::register(new FoodDelivery());\n}<\/code><\/pre>\n<p>Con este peque\u00f1o fragmento de c\u00f3digo, nuestro tipo de campo personalizado debe agregarse como una opci\u00f3n disponible en el editor de Gravity Forms. De manera predeterminada, aparece al final del cuadro &quot;Campos est\u00e1ndar&quot;. Debido a que a\u00fan no le hemos dado un nombre propio a nuestro campo (ese es el siguiente paso), el bot\u00f3n est\u00e1 etiquetado como el valor de <code>$type<\/code>.<\/p>\n<p><a href=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509dd8f563.jpg\" data-rel=\"lightbox\"><img decoding=\"async\" class=\"SDStudio-light-box-enable SDStudio-editor-tools-md-imp\" src=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509dd8f563.jpg\" alt=\"Tutorial: Cree un tipo de campo de formularios de gravedad personalizados avanzados y c\u00f3mo manejar m\u00faltiples valores de entrada\" ><\/a><\/p>\n<h2>Definici\u00f3n del nombre del campo<\/h2>\n<p>El siguiente paso es f\u00e1cil; simplemente d\u00e1ndole a nuestro campo un mejor nombre. Para hacer eso, anulamos la funci\u00f3n <code>get_form_editor_field_title()<\/code>. Todo lo que tenemos que hacer es devolver una cadena con el nombre del campo.<\/p>\n<pre><code>public function get_form_editor_field_title() {\n    return esc_attr__('Food Delivery', 'txtdomain');\n}<\/code><\/pre>\n<p>Con esta funci\u00f3n en nuestra clase, el bot\u00f3n para agregar el campo se actualiza con una etiqueta mucho mejor.<\/p>\n<h2>Cambiar la categor\u00eda del campo<\/h2>\n<p>Este paso es opcional. Por defecto, nuestro tipo de campo personalizado aparece en el cuadro &quot;Campos est\u00e1ndar&quot;, pero podemos cambiar eso. Supongamos que queremos que aparezca dentro del cuadro &quot;Campos avanzados&quot;.<\/p>\n<p>Para cambiar la categor\u00eda en la que queremos que aparezca el campo, anulamos la funci\u00f3n <code>get_form_editor_button()<\/code>. Necesitamos devolver una matriz asociativa con dos elementos. Como valor para la clave &#8216; <code>group<\/code>&#8216;, proporciona el nombre interno de la categor\u00eda en la que desea que aparezca el bot\u00f3n. Las opciones disponibles aqu\u00ed son &#8216; <code>standard_fields<\/code>&#8216;, &#8216; <code>advanced_fields<\/code>&#8216;, &#8216; <code>post_fields<\/code>&#8216; o &#8216; <code>pricing_fields<\/code>&#8216;. (Tambi\u00e9n puede crear su propia categor\u00eda, pero eso no se trata aqu\u00ed). El segundo elemento de la matriz necesita la clave &#8216; <code>text<\/code>&#8216; y para eso simplemente devolvemos el nombre del campo llamando a <code>get_form_editor_field_title()<\/code>. Esta es la funci\u00f3n que acabamos de crear arriba.<\/p>\n<pre><code>public function get_form_editor_button() {\n    return [\n        'group' =&gt; 'advanced_fields',\n        'text'  =&gt; $this-&gt;get_form_editor_field_title(),\n    ];\n}<\/code><\/pre>\n<p>Ahora el bot\u00f3n para agregar nuestro tipo de campo personalizado se mueve al cuadro &quot;Campos avanzados&quot;.<\/p>\n<h2>Activar la configuraci\u00f3n del campo<\/h2>\n<p>Si ha intentado agregar el tipo de campo en un formulario, es posible que haya notado que no hay ninguna configuraci\u00f3n. Ni siquiera puedes editar la etiqueta. La forma en que esto funciona es que todos los tipos de configuraciones realmente est\u00e1n ah\u00ed, simplemente todos est\u00e1n ocultos con CSS por Gravity Forms. Necesitamos definir individualmente qu\u00e9 configuraciones queremos habilitar, y Gravity Forms luego mostrar\u00e1 las configuraciones elegidas para nosotros.<\/p>\n<p>Necesitamos definir la funci\u00f3n <code>get_form_editor_field_settings()<\/code>y devolver una matriz de todas las configuraciones que no queremos ocultar para nuestro tipo de campo. La configuraci\u00f3n que desea agregar depende completamente de usted y su proyecto. Tenga en cuenta que su campo debe admitir cualquier configuraci\u00f3n que active; de \u200b\u200blo contrario, no tiene sentido mostrar una configuraci\u00f3n para \u00e9l.<\/p>\n<p>He creado una descripci\u00f3n general r\u00e1pida de los nombres de las configuraciones a continuaci\u00f3n. Esto est\u00e1 lejos de ser una lista completa, porque hay muchas configuraciones que son \u00fatiles solo para tipos de campos muy espec\u00edficos. Por ejemplo, formato de tel\u00e9fono, formato de fecha\/hora y un mont\u00f3n de configuraciones relacionadas con los campos de publicaci\u00f3n y precios.<\/p>\n<h4>Pesta\u00f1a General<\/h4>\n<ul>\n<li>Etiqueta de campo:<code>label_setting<\/code><\/li>\n<li>Campo Descripci\u00f3n:<code>description_setting<\/code><\/li>\n<li>Opciones:<code>choices_setting<\/code><\/li>\n<li>Requerido:<code>rules_setting<\/code><\/li>\n<li>Sin duplicados:<code>duplicate_setting<\/code><\/li>\n<li>Habilitar columnas:<code>columns_setting<\/code><\/li>\n<li>Habilite la opci\u00f3n &quot;seleccionar todo&quot;:<code>select_all_choices_setting<\/code><\/li>\n<li>Habilitar la opci\u00f3n &quot;otra&quot;:<code>other_choice_setting<\/code><\/li>\n<\/ul>\n<h4>Pesta\u00f1a de apariencia<\/h4>\n<ul>\n<li>Marcador de posici\u00f3n:<code>placeholder_setting<\/code><\/li>\n<li>Visibilidad de la etiqueta de campo y ubicaci\u00f3n de la descripci\u00f3n:<code>label_placement_setting<\/code><\/li>\n<li>Mensaje de validaci\u00f3n personalizado:<code>error_message_setting<\/code><\/li>\n<li>Clase CSS personalizada:<code>css_class_setting<\/code><\/li>\n<li>Tama\u00f1o del campo:<code>size_setting<\/code><\/li>\n<\/ul>\n<h4>Leng\u00fceta avanzada<\/h4>\n<ul>\n<li>Etiqueta de campo de administrador:<code>admin_label_setting<\/code><\/li>\n<li>Valor por defecto:<code>default_value_setting<\/code><\/li>\n<li>Habilitar entrada de contrase\u00f1a:<code>password_field_setting<\/code><\/li>\n<li>Forzar SSL:<code>force_ssl_field_setting<\/code><\/li>\n<li>Visibilidad:<code>visibility_setting<\/code><\/li>\n<li>Permitir que el campo se complete din\u00e1micamente:<code>prepopulate_field_setting<\/code><\/li>\n<li>Habilitar l\u00f3gica condicional:<code>conditional_logic_field_setting<\/code><\/li>\n<li>Habilitar l\u00f3gica condicional de p\u00e1gina:<code>conditional_logic_page_setting<\/code><\/li>\n<\/ul>\n<p>En cuanto a nuestro ejemplo, los m\u00e1s importantes son la etiqueta del campo, la descripci\u00f3n, las opciones y si el campo es obligatorio o no. Tambi\u00e9n permitimos la configuraci\u00f3n de la clase CSS, el mensaje de validaci\u00f3n personalizado y la l\u00f3gica condicional.<\/p>\n<pre><code>public function get_form_editor_field_settings() {\n    return [\n        'label_setting',\n        'choices_setting',\n        'description_setting',\n        'rules_setting',\n        'error_message_setting',\n        'css_class_setting',\n        'conditional_logic_field_setting'\n    ];\n}<\/code><\/pre>\n<p>Actualice el editor de formularios y ahora deber\u00eda ver todas las configuraciones y pesta\u00f1as elegidas dentro de nuestro campo. Todas las configuraciones son manejadas y guardadas autom\u00e1ticamente por Gravity Forms.<\/p>\n<p>Contin\u00fae y agregue algunos elementos en la lista de opciones para que tengamos algo con lo que trabajar. Esto es lo que he configurado como ejemplo:<\/p>\n<p><a href=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509df36138.png\" data-rel=\"lightbox\"><img decoding=\"async\" class=\"SDStudio-light-box-enable SDStudio-editor-tools-md-imp\" src=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509df36138.png\" alt=\"Tutorial: Cree un tipo de campo de formularios de gravedad personalizados avanzados y c\u00f3mo manejar m\u00faltiples valores de entrada\" ><\/a><\/p>\n<h2>Definici\u00f3n de opciones predeterminadas personalizadas<\/h2>\n<p>Si est\u00e1 acostumbrado a usar, por ejemplo, botones de radio o casillas de verificaci\u00f3n en Gravity Forms, probablemente haya notado que vienen con opciones como &quot;Primera opci\u00f3n&quot;, &quot;Segunda opci\u00f3n&quot;, &quot;Tercera opci\u00f3n&quot;. Este es el comportamiento predeterminado de Gravity Forms si no se han guardado opciones (antes) y esto se activa solo en estos tipos de campos espec\u00edficos. Pero para nuestro tipo de campo personalizado, no se completar\u00e1 ninguna opci\u00f3n. Esto lo hace un poco engorroso, porque no obtendr\u00e1 el bot\u00f3n &quot;+&quot; para agregar otra opci\u00f3n. Tendr\u00eda que usar el bot\u00f3n &quot;Agregar en bloque\/Opciones predefinidas&quot;, agregar algunas opciones all\u00ed y, despu\u00e9s de eso, obtener acceso a los botones &quot;+&quot; para agregar opciones. Pero es f\u00e1cil definir algunas opciones personalizadas: todo lo que necesita es definir una variable de matriz de clase<code>public $choices<\/code>y Gravity Forms generar\u00e1 autom\u00e1ticamente opciones predefinidas en su campo cuando lo agregue a sus formularios.<\/p>\n<p>Nota: Esta es una variable de clase, que puede agregar en la parte superior de la clase, justo debajo <code>public $type<\/code>. Cada opci\u00f3n debe ser una matriz, con la opci\u00f3n como valor para la clave &#8216; <code>text<\/code>&#8216;.<\/p>\n<pre><code>public $choices = [\n    [ 'text' =&gt; 'Food Choice 1' ],\n    [ 'text' =&gt; 'Food Choice 2' ],\n    [ 'text' =&gt; 'Food Choice 3' ],\n];<\/code><\/pre>\n<p>Tenga en cuenta que si ya ha agregado el campo al formulario, no se completar\u00e1n las opciones de forma retroactiva. Esto solo entra en vigencia cuando agrega un nuevo campo al formulario.<\/p>\n<p><strong>Nota<\/strong>: en Gravity Forms parece posible agregar tambi\u00e9n las teclas &#8216; <code>value<\/code>&#8216; a cada opci\u00f3n. Pero no he conseguido que esto funcione: los valores se convertir\u00e1n autom\u00e1ticamente en los mismos que el texto de elecci\u00f3n.<\/p>\n<h2>Definici\u00f3n del valor del campo como matriz<\/h2>\n<p>El siguiente paso es bastante simple, pero necesario. Como valores predeterminados para los campos en Gravity Forms son cadenas. Necesitamos que el valor sea una matriz porque trabajamos con m\u00faltiples entradas. Para ello definimos la funci\u00f3n <code>is_value_submission_array()<\/code>y devolvemos <code>true<\/code>.<\/p>\n<pre><code>public function is_value_submission_array() {\n    return true;\n}<\/code><\/pre>\n<p>Esto asegura que podamos trabajar correctamente con el valor ingresado de nuestras entradas m\u00faltiples.<\/p>\n<h2>Representaci\u00f3n de la salida del campo<\/h2>\n<p>Cuando se trata de representar la salida del campo, hay un par de cosas a tener en cuenta.<\/p>\n<p>En primer lugar, debe elegir entre dos funciones; <code>get_field_input()<\/code>o <code>get_field_content()<\/code>. En el primer m\u00e9todo, Gravity Forms muestra autom\u00e1ticamente el elemento de la lista envolvente, la etiqueta, la descripci\u00f3n y el contenedor para el mensaje de error de validaci\u00f3n en su campo y usted solo controla la salida del campo interno. Con el segundo m\u00e9todo, nada de esto sucede y tiene m\u00e1s control sobre la salida del campo. Sin embargo, debe representar manualmente la etiqueta, la descripci\u00f3n y los mensajes de error. El primer m\u00e9todo, <code>get_field_input()<\/code>, est\u00e1 perfectamente bien para la mayor\u00eda de los casos.<\/p>\n<p>La segunda cosa a tener en cuenta es que la funci\u00f3n de representaci\u00f3n del campo afecta a tres ubicaciones diferentes. Los tres son la representaci\u00f3n de la salida del campo en la interfaz, la vista previa del campo dentro del editor de formularios y, finalmente, tambi\u00e9n el campo al editar una entrada. Por suerte Gravity Forms ofrece funciones para determinar f\u00e1cilmente en qu\u00e9 vista nos encontramos. Por lo general, renderizar\u00eda el campo de la misma manera en los tres casos. Pero debido a que la representaci\u00f3n de una tabla grande con muchas entradas se vuelve innecesariamente torpe dentro del editor de formularios, he optado por representar el campo de manera diferente dentro del editor de formularios.<\/p>\n<p>Y finalmente, debemos asegurarnos de que cualquier entrada obtenga un <code>name<\/code>atributo adecuado para que Gravity Forms pueda recopilar su valor al enviar el formulario. Todas las entradas en Gravity Forms necesitan <code>name<\/code>atributos que sigan esta regla: <code>name=\"input_{FIELD_ID}\"<\/code>(los campos de selecci\u00f3n m\u00faltiple usan una identificaci\u00f3n adicional, pero no necesitamos preocuparnos por eso en nuestro caso). Tenemos acceso al ID de campo ya que es una variable de clase (de <code>GF_Field<\/code>). Pero en nuestro caso, le hemos dicho a Gravity Forms que el valor es una matriz y no un valor singular (paso anterior), por lo que agregamos corchetes despu\u00e9s del atributo de nombre; <code>name=\"input_{FIELD_ID}[]\"<\/code>. Entonces, si el campo tiene la ID de 4 dentro de un formulario, el atributo de nombre debe ser &quot; <code>input_4[]<\/code>&quot;.<\/p>\n<p>Estoy optando por usar <code>get_field_input()<\/code>que viene con tres par\u00e1metros. El primer par\u00e1metro es el objeto de formulario, que en realidad no necesitamos para nuestro ejemplo. El segundo par\u00e1metro es el valor actual. Puede ser el valor del campo desde <code>$_POST<\/code>el momento en que se intent\u00f3 enviar el formulario, pero sin \u00e9xito. Podemos conservar los valores enviados anteriormente. O si la funci\u00f3n se est\u00e1 ejecutando al editar una entrada, el valor ser\u00e1 el valor almacenado del env\u00edo. Trataremos el valor m\u00e1s de cerca m\u00e1s adelante. Y el tercer par\u00e1metro es el objeto de entrada, que tampoco necesitaremos para nuestro ejemplo.<\/p>\n<p>Comencemos a implementar <code>get_field_input()<\/code>cu\u00e1l espera el renderizado final como una cadena. Directamente desde el principio, decido devolver una cadena vac\u00eda si estamos dentro del editor de formularios, porque no quiero mostrar la tabla completa en esta vista. Podemos usar el m\u00e9todo <code>$this-&gt;is_form_editor()<\/code>para verificar si estamos o no dentro de la edici\u00f3n del formulario. Puede optar por omitir esto o representar algo m\u00e1s si desea una vista previa del campo dentro del editor de formularios.<\/p>\n<pre><code>public function get_field_input($form, $value = '', $entry = null) {\n    if ($this-&gt;is_form_editor()) {\n        return '';\n    }\n    \/\/ .. Rest of code for frontend and edit entry here...\n}<\/code><\/pre>\n<p>El siguiente paso es construir el HTML para una tabla que recorre una serie de d\u00edas para generar las columnas y las filas para cada elemento del curso. Pero debido a que necesitamos acceso a la matriz de d\u00edas (columnas de la tabla) en varios lugares, debemos definirla como una variable de clase, haci\u00e9ndola accesible desde cualquier funci\u00f3n dentro de ella. Defino una variable de clase <code>$delivery_days<\/code>con una matriz de los d\u00edas para los que quiero ofrecer la entrega.<\/p>\n<pre><code>class FoodDelivery extends GF_Field {\n    public $type = 'food_delivery';\n\u00a0\n    private $delivery_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];\n\u00a0\n    public function get_form_editor_field_title() {\n        ...\n}<\/code><\/pre>\n<p>\u00a1Esto es solo un ejemplo! Es posible que desee obtener la matriz de las columnas de otro lugar que no est\u00e9 codificado.<\/p>\n<p>Volvamos <code>get_field_input()<\/code>y construyamos nuestra tabla con entradas. Primero recorro la variable de clase y genero los encabezados de la tabla. Luego repaso las opciones ingresadas en la configuraci\u00f3n de campo para Elecciones. Esto es accesible desde la variable de clase (desde <code>GF_Field<\/code>) <code>$this-&gt;choices<\/code>. Para cada opci\u00f3n, presento una entrada con los atributos de nombre propios. Tenemos acceso al ID del campo desde <code>GF_Field<\/code>la variable de clase de <code>$this-&gt;id<\/code>.<\/p>\n<pre><code>public function get_field_input($form, $value = '', $entry = null) {\n    if ($this-&gt;is_form_editor()) {\n        return '';\n    }\n\u00a0\n    $id = (int) $this-&gt;id;\n    $table = '&lt;table class=\"delivery-table\"&gt;&lt;tbody&gt;&lt;tr&gt;';\n    $table .= '&lt;th&gt;'. __('Course', 'txtdomain'). '&lt;\/th&gt;';\n    foreach ($this-&gt;delivery_days as $day) {\n        $table .= '&lt;th&gt;'. $day. '&lt;\/th&gt;';\n    }\n    $table .= '&lt;\/tr&gt;';\n\u00a0\n    foreach ($this-&gt;choices as $course) {\n        $table .= '&lt;tr&gt;';\n        $table .= '&lt;td&gt;'. $course['text']. '&lt;\/td&gt;';\n        foreach ($this-&gt;delivery_days as $day) {\n            $table .= '&lt;td&gt;&lt;input type=\"number\" size=\"1\" name=\"input_'. $id. '[]\" \/&gt;&lt;\/td&gt;';\n        }\n        $table .= '&lt;\/tr&gt;';\n    }\n    $table .= '&lt;\/tbody&gt;&lt;\/table&gt;';\n\u00a0\n    return $table;\n}<\/code><\/pre>\n<p>\u00a1Con este c\u00f3digo en su lugar, deber\u00edamos obtener una buena tabla representada para nuestro tipo de campo en la interfaz! Obviamente, el HTML depende totalmente de usted, este es solo un ejemplo b\u00e1sico.<\/p>\n<p><strong>Dejamos esta funci\u00f3n por ahora, \u00a1pero volveremos a ella m\u00e1s tarde para manejar el valor enviado!<\/strong><\/p>\n<h2>Almacenar el valor correctamente<\/h2>\n<p>A partir de ahora, Gravity Forms guardar\u00e1 nuestro campo como una matriz unidimensional poblada con los valores ingresados \u200b\u200by cadenas vac\u00edas donde la entrada estaba vac\u00eda. No hay informaci\u00f3n sobre a qu\u00e9 d\u00eda o elecci\u00f3n pertenece el valor, aparte del \u00edndice. Necesitamos transformar esta matriz unidimensional en una matriz asociativa multidimensional donde almacenamos el d\u00eda y la etiqueta de elecci\u00f3n. Entonces podemos acceder f\u00e1cilmente al valor del n\u00famero almacenado, por ejemplo <code>$value['Ham sandwich']['Monday']<\/code>. Despu\u00e9s de esta transformaci\u00f3n de matriz, tambi\u00e9n necesitamos serializar la matriz para que Gravity Forms pueda almacenar el valor correctamente en la base de datos.<\/p>\n<p>Tendremos que transformar esta matriz de valores en varios lugares, as\u00ed que definir\u00e9 una funci\u00f3n separada para esto. La funci\u00f3n acepta la matriz unidimensional y la transforma en una matriz multidimensional con los valores almacenados para d\u00edas y opciones:<\/p>\n<pre><code>private function translateValueArray($value) {\n    if (empty($value)) {\n        return [];\n    }\n    $table_value = [];\n    $counter = 0;\n    foreach ($this-&gt;choices as $course) {\n        foreach ($this-&gt;delivery_days as $day) {\n            $table_value[$course['text']][$day] = $value[$counter++];\n        }\n    }\n    return $table_value;\n}<\/code><\/pre>\n<p>Esto almacenar\u00e1 los nombres de los d\u00edas y las opciones directamente dentro del valor del campo. Hacerlo de esta manera hace posible cambiar las opciones en un momento posterior sin romper las entradas anteriores.<\/p>\n<p>Ahora pasemos a anular la funci\u00f3n que maneja el almacenamiento del valor enviado; <code>get_value_save_entry()<\/code>. Viene con cinco par\u00e1metros, pero solo necesitamos el primero, que es el valor enviado. Dentro de la funci\u00f3n, pasamos el valor a nuestra funci\u00f3n personalizada anterior, serializamos su devoluci\u00f3n y finalmente devolvemos el nuevo valor.<\/p>\n<pre><code>public function get_value_save_entry($value, $form, $input_name, $lead_id, $lead) {\n    if (empty($value)) {\n        $value = '';\n    } else {\n        $table_value = $this-&gt;translateValueArray($value);\n        $value = serialize($table_value);\n    }\n    return $value;\n}<\/code><\/pre>\n<p>\u00a1En este punto, Gravity Forms almacenar\u00e1 con \u00e9xito nuestros valores tal como los queremos! Sin embargo, el valor almacenado ahora es una matriz serializada que Gravity Forms felizmente reproducir\u00e1 directamente. Necesitamos implementar funciones para transformarlo de una matriz serializada fea en una salida bonita donde sea que la necesitemos.<\/p>\n<h2>Mostrando el valor enviado<\/h2>\n<p>Hay tres lugares en los que necesitamos cambiar la salida del valor de nuestro campo; la lista de entradas, mirando una sola entrada y dentro de las etiquetas de combinaci\u00f3n de Gravity Forms. Las etiquetas de combinaci\u00f3n se utilizan con mayor frecuencia en las notificaciones por correo electr\u00f3nico. Por ejemplo <code>{all_fields}<\/code>, es una etiqueta de combinaci\u00f3n que muestra los valores completos del formulario enviado en los correos electr\u00f3nicos.<\/p>\n<p>Debido a que estamos representando la misma salida en tres casos diferentes, tiene sentido crear una funci\u00f3n separada para ella. He definido una funci\u00f3n personalizada que acepta el valor; la matriz multidimensional no serializada, como par\u00e1metro. Luego, la funci\u00f3n genera algo de HTML que muestra la matriz de una manera bonita y devuelve la cadena. He optado por una <code>&lt;ul&gt;<\/code>lista anidada, pero puede cambiar la salida como desee.<\/p>\n<pre><code>private function prettyListOutput($value) {\n    $str = '&lt;ul&gt;';\n    foreach ($value as $course =&gt; $days) {\n        $week = '';\n        foreach ($days as $day =&gt; $delivery_number) {\n            if (!empty($delivery_number)) {\n                $week .= '&lt;li&gt;'. $day. ': '. $delivery_number. '&lt;\/li&gt;';\n            }\n        }\n        \/\/ Only add week if there were any requests at all\n        if (!empty($week)) {\n            $str .= '&lt;li&gt;&lt;h3&gt;'. $course. '&lt;\/h3&gt;&lt;ul class=\"days\"&gt;'. $week. '&lt;\/ul&gt;&lt;\/li&gt;';\n        }\n    }\n    $str .= '&lt;\/ul&gt;';\n    return $str;\n}<\/code><\/pre>\n<p>Genial, empecemos por la primera: la lista de entradas: <code>get_value_entry_list()<\/code>. Puede optar por generar la salida completa aqu\u00ed, pero puede volverse bastante torpe y largo para la vista de lista, por lo que opt\u00e9 por devolver una cadena fija que explica que el usuario debe ingresar a los detalles de la entrada para ver la descripci\u00f3n completa.<\/p>\n<pre><code>public function get_value_entry_list($value, $entry, $field_id, $columns, $form) {\n    return __('Enter details to see delivery details', 'txtdomain');\n}<\/code><\/pre>\n<p>Por supuesto, esto depende totalmente de usted, puede optar por mostrar solo el primer n\u00famero x de caracteres, por ejemplo.<\/p>\n<p>La segunda funci\u00f3n es la que afecta la vista de una sola entrada <code>get_value_entry_detail()<\/code>:<\/p>\n<pre><code>public function get_value_entry_detail($value, $currency = '', $use_text = false, $format = 'html', $media = 'screen') {\n    $value = maybe_unserialize($value);     \n    if (empty($value)) {\n        return '';\n    }\n    $str = $this-&gt;prettyListOutput($value);\n    return $str;\n}<\/code><\/pre>\n<p>Simplemente deserializamos la matriz con la funci\u00f3n de WordPress <code>[maybe_unserialize](https:\/\/developer.wordpress.org\/reference\/functions\/maybe_unserialize\/)()<\/code>y devolvemos la cadena de salida de nuestra funci\u00f3n personalizada.<\/p>\n<p>La funci\u00f3n final afecta las etiquetas de combinaci\u00f3n y se asegura de que el valor de nuestro campo tambi\u00e9n se vea bien dentro de los correos electr\u00f3nicos: <code>get_value_merge_tag()<\/code>.<\/p>\n<pre><code>public function get_value_merge_tag($value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br) {\n    return $this-&gt;prettyListOutput($value);\n}<\/code><\/pre>\n<p>Tenga en cuenta que no necesitaremos deserializar el valor dentro de esta funci\u00f3n.<\/p>\n<p>\u00a1Con estas tres funciones en su lugar, todos los valores enviados deber\u00edan verse bastante bien en todas partes! Por ejemplo, al ver una entrada enviada:<\/p>\n<p><a href=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509dbe8f8c.png\" data-rel=\"lightbox\"><img decoding=\"async\" class=\"SDStudio-light-box-enable SDStudio-editor-tools-md-imp\" src=\"https:\/\/wordpress.mediadoma.com\/wp-content\/uploads\/2022\/01\/post-153179-61e509dbe8f8c.png\" alt=\"Tutorial: Cree un tipo de campo de formularios de gravedad personalizados avanzados y c\u00f3mo manejar m\u00faltiples valores de entrada\" ><\/a><\/p>\n<p>Sin embargo, \u00a1falta una cosa importante! En este punto, nuestras entradas no conservan los valores enviados anteriormente y eso es bastante malo.<\/p>\n<h2>Hacer que nuestras entradas conserven el valor enviado anteriormente<\/h2>\n<p>Hay principalmente dos casos en los que debemos asegurarnos de que las entradas mantengan los valores enviados anteriormente. El primer caso es cuando falla el env\u00edo de un formulario (por ejemplo, el usuario olvid\u00f3 un campo obligatorio). En este momento, todas nuestras entradas pierden todos los valores ingresados \u200b\u200bpreviamente y el usuario tiene que volver a ingresar todos los valores nuevamente. En segundo lugar, cuando el propietario del sitio edita una entrada, las entradas no se completan con los valores enviados desde el env\u00edo, lo que hace que sea bastante imposible editar los valores correctamente.<\/p>\n<p>Para solucionar esto volvemos a la funci\u00f3n <code>get_field_input()<\/code>. El segundo par\u00e1metro de esta funci\u00f3n es el valor. Pero recuerda que esta funci\u00f3n afecta tanto al renderizado frontend como a la edici\u00f3n de entrada. Esto es importante porque el valor almacenado es diferente en estos dos casos. Si estamos en la interfaz y manejando el env\u00edo de formularios, el valor est\u00e1 en el formato de la matriz unidimensional mencionada anteriormente. Y si estamos editando una entrada, el valor tiene el formato de una matriz multidimensional serializada. Por lo tanto, debemos traducir correctamente el valor proporcionado <code>get_field_input()<\/code>para acceder f\u00e1cilmente a los valores reales.<\/p>\n<pre><code>public function get_field_input($form, $value = '', $entry = null) {\n    if ($this-&gt;is_form_editor()) {\n        return '';\n    }\n\u00a0\n    $id = (int) $this-&gt;id;\n    if ($this-&gt;is_entry_detail()) {\n        $table_value = maybe_unserialize($value);\n    } else {\n        $table_value = $this-&gt;translateValueArray($value);\n    }\n\u00a0\n    $table = '&lt;table class=\"delivery-table\"&gt;&lt;tbody&gt;&lt;tr&gt;';\n    ...\n}<\/code><\/pre>\n<p>En el c\u00f3digo anterior, antes de comenzar a crear el HTML para la salida del campo, creamos una variable <code>$table_value<\/code>que contiene el valor traducido correctamente. Usamos <code>GF_Field<\/code>la funci\u00f3n <code>is_entry_detail()<\/code>para verificar si estamos editando una entrada o no. Y luego, para nuestras entradas, es f\u00e1cil acceder a los valores adecuados y establecerlos como <code>value<\/code>atributos de las entradas:<\/p>\n<pre><code>...\nforeach ($this-&gt;delivery_days as $day) {\n    $table .= '&lt;td&gt;&lt;input type=\"number\" size=\"1\" name=\"input_'. $id. '[]\" value=\"'. $table_value[$course['text']][$day]. '\" \/&gt;&lt;\/td&gt;';\n}\n...<\/code><\/pre>\n<p>Con lo anterior actualizado <code>get_field_input()<\/code>, todas nuestras entradas personalizadas siempre deben completarse con el valor anterior; no importa si est\u00e1 editando una entrada o reintentando el env\u00edo de un formulario.<\/p>\n<p>En este punto, todo lo relacionado con la representaci\u00f3n y el almacenamiento de nuestros valores est\u00e1 hecho y en pleno funcionamiento. Pero hay una cosa m\u00e1s que definitivamente debemos arreglar.<\/p>\n<h2>Hacer que nuestro campo pase la validaci\u00f3n &quot;requerida&quot;<\/h2>\n<p>Los formularios de gravedad tienen comprobaciones para ver si el valor de un campo est\u00e1 vac\u00edo o no. Esto suele ser necesario cuando el campo se establece como obligatorio. Cuando se requiere un campo, no puede enviar el formulario si est\u00e1 vac\u00edo, \u00bfverdad? El problema para nosotros es que tenemos m\u00faltiples entradas y queremos permitir que algunas de ellas est\u00e9n vac\u00edas. Esto se convierte en un problema si nuestro campo se establece en requerido. Desafortunadamente, Gravity Forms interpreta incorrectamente &quot;est\u00e1 vac\u00edo&quot; y requiere que se completen todas las entradas. Por lo tanto, debemos agregar una regla que diga que si se completa al menos una de nuestras muchas entradas, el valor total del campo no est\u00e1 vac\u00edo.<\/p>\n<p>La funci\u00f3n final que necesitamos anular en nuestra clase es <code>is_value_submission_empty()<\/code>. Solo obtenemos el ID del formulario como par\u00e1metro para esta funci\u00f3n, por lo que debemos extraer el valor del campo utilizando la funci\u00f3n Gravity Forms para obtenerlo de la <code>$_POST<\/code>matriz: <code>rgpost('input_&lt;FIELD ID&gt;')<\/code>. El retorno debe ser la matriz unidimensional que hemos visto antes. Todo lo que tenemos que hacer es recorrer la matriz y regresar <code>false<\/code>si encontramos un valor en alguna parte. De lo contrario, regresamos <code>true<\/code>ya que el valor del campo est\u00e1 completamente vac\u00edo.<\/p>\n<pre><code>public function is_value_submission_empty($form_id) {\n    $value = rgpost('input_'. $this-&gt;id);\n    foreach ($value as $input) {\n        if (strlen(trim($input)) &gt; 0) {\n            return false;\n        }\n    }\n    return true;\n}<\/code><\/pre>\n<p>Con la funci\u00f3n anterior en su lugar, nuestro campo no fallar\u00e1 en el env\u00edo si est\u00e1 configurado como obligatorio y se completa al menos una entrada.<\/p>\n<h2>Conclusi\u00f3n y c\u00f3digo final<\/h2>\n<p>Este tutorial le ha mostrado en detalle c\u00f3mo crear su propio tipo de campo avanzado personalizado para Gravity Forms. Incluso si su proyecto es diferente a mi ejemplo, espero que tenga algunos consejos y a-ha en el camino. \u00a1Encuentro que la documentaci\u00f3n de Gravity Forms es bastante deficiente en algunos casos, y este es el resultado de muchas pruebas y errores! De todos modos, \u00a1espero que esto haya sido de alguna utilidad para ti!<\/p>\n<p>Como referencia, aqu\u00ed est\u00e1 el c\u00f3digo completo en su totalidad:<\/p>\n<pre><code>if (class_exists('GF_Field')) {\n    class FoodDelivery extends GF_Field {\n        public $type = 'food_delivery';\n\u00a0\n        public $choices = [\n            [ 'text' =&gt; 'Food Choice 1' ],\n            [ 'text' =&gt; 'Food Choice 2' ],\n            [ 'text' =&gt; 'Food Choice 3' ],\n        ];\n\u00a0\n        private $delivery_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];\n\u00a0\n        public function get_form_editor_field_title() {\n            return esc_attr__('Food Delivery', 'txtdomain');\n        }\n\u00a0\n        public function get_form_editor_button() {\n            return [\n                'group' =&gt; 'advanced_fields',\n                'text'  =&gt; $this-&gt;get_form_editor_field_title(),\n            ];\n        }\n\u00a0\n        public function get_form_editor_field_settings() {\n            return [\n                'label_setting',\n                'choices_setting',\n                'description_setting',\n                'rules_setting',\n                'error_message_setting',\n                'css_class_setting',\n                'conditional_logic_field_setting',\n            ];\n        }\n\u00a0\n        public function is_value_submission_array() {\n            return true;\n        }\n\u00a0\n        public function get_field_input($form, $value = '', $entry = null) {\n            if ($this-&gt;is_form_editor()) {\n                return '';\n            }\n\u00a0\n            $id = (int) $this-&gt;id;\n            if ($this-&gt;is_entry_detail()) {\n                $table_value = maybe_unserialize($value);\n            } else {\n                $table_value = $this-&gt;translateValueArray($value);\n            }\n\u00a0\n            $table = '&lt;table class=\"delivery-table\"&gt;&lt;tbody&gt;&lt;tr&gt;';\n            $table .= '&lt;th&gt;'. __('Course', 'txtdomain'). '&lt;\/th&gt;';\n            foreach ($this-&gt;delivery_days as $day) {\n                $table .= '&lt;th&gt;'. $day. '&lt;\/th&gt;';\n            }\n            $table .= '&lt;\/tr&gt;';\n\u00a0\n            foreach ($this-&gt;choices as $course) {\n                $table .= '&lt;tr&gt;';\n                $table .= '&lt;td&gt;'. $course['text']. '&lt;\/td&gt;';\n                foreach ($this-&gt;delivery_days as $day) {\n                    $table .= '&lt;td&gt;&lt;input type=\"number\" size=\"1\" name=\"input_'. $id. '[]\" value=\"'. $table_value[$course['text']][$day]. '\" \/&gt;&lt;\/td&gt;';\n                }\n                $table .= '&lt;\/tr&gt;';\n            }\n\u00a0\n            $table .= '&lt;\/tbody&gt;&lt;\/table&gt;';\n\u00a0\n            return $table;\n        }\n\u00a0\n        private function translateValueArray($value) {\n            if (empty($value)) {\n                return [];\n            }\n            $table_value = [];\n            $counter = 0;\n            foreach ($this-&gt;choices as $course) {\n                foreach ($this-&gt;delivery_days as $day) {\n                    $table_value[$course['text']][$day] = $value[$counter++];\n                }\n            }\n            return $table_value;\n        }\n\u00a0\n        public function get_value_save_entry($value, $form, $input_name, $lead_id, $lead) {\n            if (empty($value)) {\n                $value = '';\n            } else {\n                $table_value = $this-&gt;translateValueArray($value);\n                $value = serialize($table_value);\n            }\n            return $value;\n        }\n\u00a0\n        private function prettyListOutput($value) {\n            $str = '&lt;ul&gt;';\n            foreach ($value as $course =&gt; $days) {\n                $week = '';\n                foreach ($days as $day =&gt; $delivery_number) {\n                    if (!empty($delivery_number)) {\n                        $week .= '&lt;li&gt;'. $day. ': '. $delivery_number. '&lt;\/li&gt;';\n                    }\n                }\n                \/\/ Only add week if there were any requests at all\n                if (!empty($week)) {\n                    $str .= '&lt;li&gt;&lt;h3&gt;'. $course. '&lt;\/h3&gt;&lt;ul class=\"days\"&gt;'. $week. '&lt;\/ul&gt;&lt;\/li&gt;';\n                }\n            }\n            $str .= '&lt;\/ul&gt;';\n            return $str;\n        }\n\u00a0\n        public function get_value_entry_list($value, $entry, $field_id, $columns, $form) {\n            return __('Enter details to see delivery details', 'txtdomain');\n        }\n\u00a0\n        public function get_value_entry_detail($value, $currency = '', $use_text = false, $format = 'html', $media = 'screen') {\n            $value = maybe_unserialize($value);     \n            if (empty($value)) {\n                return $value;\n            }\n            $str = $this-&gt;prettyListOutput($value);\n            return $str;\n        }\n\u00a0\n        public function get_value_merge_tag($value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br) {\n            return $this-&gt;prettyListOutput($value);\n        }\n\u00a0\n        public function is_value_submission_empty($form_id) {\n            $value = rgpost('input_'. $this-&gt;id);\n            foreach ($value as $input) {\n                if (strlen(trim($input)) &gt; 0) {\n                    return false;\n                }\n            }\n            return true;\n        }\n    }\n    GF_Fields::register(new FoodDelivery());\n}<\/code><\/pre>\n<p><div id=\"PostUnique_PostSource\" style=\"padding-top: 50px\">Fuente de grabaci\u00f3n:  <a target=\"_blank\" rel=\"noopener nofollow\" href=\"\/\/awhitepixel.com\" class=\"external external_icon\">awhitepixel.com<\/a><\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Un tutorial que muestra c\u00f3mo crear un tipo de campo Gravity Forms personalizado avanzado con m\u00faltiples entradas con manejo especial para almacenar y mostrar los valores.<\/p>\n","protected":false},"author":1,"featured_media":153180,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_wp_rev_ctl_limit":""},"categories":[892,892,810,1110,800,800,810,840,840,861,861],"tags":[1172],"class_list":{"0":"post-233468","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","6":"hentry","7":"category-codigo","9":"category-complementos","10":"category-n-a","11":"category-php-2","14":"category-tutoriales","16":"category-wordpress-2","18":"tag-affiai-es"},"_links":{"self":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/posts\/233468","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/comments?post=233468"}],"version-history":[{"count":0,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/posts\/233468\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/media\/153180"}],"wp:attachment":[{"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/media?parent=233468"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/categories?post=233468"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wordpress.mediadoma.com\/es\/wp-json\/wp\/v2\/tags?post=233468"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}