Enviar un formulario de 50 campos a varias tablas; POST regular, AJAX POST u otro?

Lector de largo tiempo de Stackoverflow; Póster por primera vez, así que espero que seas amable 🙂

Tengo un formulario en una página que consta de aproximadamente 50 campos de diferentes tipos (casillas de verificación / texto / decimal / fecha, etc.). Los valores se extraen de unas 8 tablas a través de una consulta, aproximadamente:

SELECT * FROM p LEFT JOIN pd on p.id=pd.id LEFT JOIN pc on p.id=pc.id LEFT JOIN pie on p.id=pie.id etc. WHERE p.id = xxx 

Comencé el día pensando que solo usaría un POST simple en el formulario, escribía un montón de validación y actualizaba las consultas (anulando cada valor existente con lo que sea que estuviera en el formulario) y terminaría con él, pero cuestiono mi opinión aquí.

Específicamente, se siente mal estar anulando un valor existente si no ha cambiado, y estoy un poco preocupado por lo que sucederá si la actualización de la base de datos falla a mitad de camino (pensando en manejar eso con transacciones). Me siento cómodo con esto en formularios más pequeños, pero si el personal solo ha cambiado 1 o 2 campos, esto se siente como un montón de escritura para nada. Mi siguiente pensamiento fue hacer AJAX basado en un nivel por campo. Al cambiar cualquier campo se envía el cambio y se guarda. Parece que podría tener más sentido, incluso si prefiero evitar js si pudiera. Una tercera opción, por supuesto, es convertirlo en múltiples formularios con múltiples botones de envío, digamos uno por pestaña (el formulario ya está dividido en tabs), con el lado negativo recargando la página más a menudo, ya que necesita más envío (aunque aquí Por supuesto, también se podría utilizar AJAX).

¿Debería incluso estar pensando tanto en esto (pasé la mayor parte del día leyendo hasta aquí en viejos hilos …)? Aquí hay un poco de información financiera, por lo que mis principales preocupaciones son la confiabilidad y el rendimiento, pero también tengo curiosidad por saber si hay algún tipo de práctica recomendada que otros sigan.

— ACTUALIZACIÓN DESPUÉS DE IMPLEMENTAR LA RESPUESTA ELEGIDA ABAJO —

Siendo un lector de SO desde hace mucho tiempo, siempre aprecio los hilos en los que la persona que hace la pregunta sigue más adelante, así que pensé que lo haría yo mismo. No estoy seguro de protocolo o formato correcto.

Según lo anterior, terminé yendo con la solución de barnyr, que básicamente usa javascript para comparar el formulario al enviar con los valores originales y luego publica los cambios en mysql (usando jquery post). Aquí hay algunas cosas en que pensar si está considerando un escenario similar:

  1. Fuera del bate, la serialización de jquery no envía los valores de la checkbox / radio si no están seleccionados. Veo su lógica, pero para mí esto no tiene sentido. Usé el complemento en http://tdanemar.wordpress.com/2010/08/24/jquery-serialize-method-and-checkboxes/ para resolver esto.

  2. Si edita un valor en la página, luego lo guarda y luego lo edita nuevamente, volviendo al valor original, recibirá un mensaje de “nada cambiado” en comparación con los valores iniciales establecidos en la carga de la página, nada ha cambiado. Es lógico, pero no había considerado esto hasta después de que terminé y probé. Realmente no veo una forma de evitar esto que justifique mantener la complejidad que viene con esta solución a través de un simple ‘anular todo en el formulario enviado’, por lo que si está creando una aplicación pública en la que se preocupa por sus usuarios, NO lo haría. Recomiendo que uses esta metodología.

  3. En términos de normalización, esta solución es hermosa ya que puedo evitar que las filas se agreguen a las tablas vinculadas a la principal que contiene el ID de usuario, a menos que el contenido se agregue a esos campos específicos. Sin embargo, si el punto 2 era un gran problema para mí, eliminaría mucha complejidad del código para almacenar todos estos valores que se muestran en la única forma grande en una tabla grande. Soy prácticamente una normalización en el campo de los principiantes (así que no te preocupes por las horcas) y esto es, por supuesto, principalmente una consecuencia de que todos los datos se muestran en una sola forma. Si está utilizando formularios múltiples, esto ya no se aplica.

  4. Parte del sistema involucra muchos números, sumdos en otras partes del formulario, y hacer todo esto a través de AJAX significa que debe ser muy cuidadoso y claro qué está cambiando exactamente cuando el usuario pulsa guardar. Por ejemplo, pueden cambiar el precio del progtwig, y ​​el precio total también debería actualizarse, pero a medida que realiza el envío a través de AJAX y no hay una recarga adecuada, debe volver a ingresar todo esto al sistema.

Los puntos 2,3 y 4 podrían tratarse en este caso, pero si hubiera sabido lo que sé ahora al comenzar, probablemente habría elegido una tabla grande con todos los datos y una simple edición de la fila completa en el formulario enviado. Por otra parte, habría aprendido mucho menos, por lo que no me arrepiento 🙂 Espero que esto sea de ayuda para aquellos que encuentran este hilo en una etapa posterior.

ACTUALIZACIÓN: el enfoque inicial no manejó otros tipos de entrada tan bien. He cambiado el código para manejar tipos de entrada comunes, así como el uso de las propiedades DOM para los valores iniciales, lo que evita haber ejecutado cualquier código en la carga de la página:

Aquí está el enlace: http://jsfiddle.net/rLwca/5/ y aquí está la función actualizada:

  //Initial setup no longer needed. the DOM has the default states anyway... //heres where we filter the elements for ones which have changed $("#My50PageForm").submit(function(){ var elems = $("#My50PageForm :input").filter(function(value){ var elem=$(this), type=this.tagName +"_"+(elem.attr("type")||""); // uniquely name the element tag and type switch (type){ case "INPUT_radio": case "INPUT_checkbox": return elem.prop("checked")!=elem.prop("defaultChecked"); case "INPUT_text": case "INPUT_textarea": case "INPUT_": return elem.val()!=elem.prop("defaultValue"); case "SELECT_": var options=$(this).find('option'), defaultValue=options.first().val(); //use the first element's value as default in case no defaults are set options.each(function (i,o) { defaultValue=this.defaultSelected?this.value:defaultValue; }); return $(this).val()!=defaultValue; default: console.log("type "+type+" not handled"); return false; } }); if(elems.length){ console.log(elems.serialize()); return false; $.post("http://jsfiddle.net/example.cfm", elems.serialize()); }else{ alert("nothing changed"); } return false; }); 

Código original a continuación:

Aquí hay un enlace a un ejemplo mínimo de enviar lo que ha cambiado:

http://jsfiddle.net/UhGQX/

 $(document).ready(function(){ //Copy all form valued into a data attribute called 'original' when the page loads $("#My50PageForm :input").each(function(elem){ $(this).data("original",$(this).val()); }); //here's where you check what has changed $("#My50PageForm").submit(function(){ var elems = $("#My50PageForm :input").filter(function(value){ var elem=$(this), original=elem.data("original"); console.log(original); //check that original isn't 'undefined' and that it's been changed return original && elem.val()!==original }); if(elems.length){ //post the data back to your server for processing $.post("http://jsfiddle.net/example.cfm", elems.serialize()); }else{ alert("nothing changed"); } return false; }); }); 

Los bits clave son:

  • Cuando se carga la página, use jQuery para hacer una copia de los valores iniciales de cada campo de formulario
  • cuando se activa el envío, compare el valor actual de cada campo con el que se guardó cuando se cargó la página.
  • Si hay cambios, vuelva a enviar los datos al servidor.

Otros enfoques podrían ser, permitiendo que todo el formulario sea publicado:

  • almacenar los datos en el servidor en sesión
  • vuelva a ejecutar la selección que utilizó para completar la página, luego compárela con lo que se ha publicado en su formulario

Bueno, compararía los valores que están en la página con los valores que el usuario ha cambiado. Luego, enviaría los valores modificados al servidor, crearía mi consulta dinámicamente y actualizaría solo los campos que habían cambiado.

Además, definitivamente debería usar transacciones si está actualizando varias tablas.

Además de la respuesta de Jordan, diría que el mejor lugar para comenzar es con las expectativas de los usuarios. Cada una de las diferentes opciones funcionará técnicamente, pero todas tienen diferentes comportamientos en términos de lo que ahorran y cuándo.

Me aseguraría de que todos sean aceptables para quien sea la parte interesada / propietario del producto / analista / jefe en esta parte de la funcionalidad. Sería muy molesto tener que volver a codificar todo esto porque la empresa decide que guardar campo por campo no es aceptable (y es el tipo de cosa que nunca se considera hasta que alguien usa su interfaz de usuario)

Evitaría las escrituras de base de datos basadas en el cliente onChange o onBlur events. Si bien es fácil de codificar, se basa en el supuesto de que el usuario realmente ha decidido el valor apropiado para ese campo. Esa es una suposición insegura. Algunas personas revisan. Otros abandonan las formas a medio camino. Además, si el campo de formulario es una selección, el evento onChange puede activarse antes de que lo haga.

También evitaría almacenar datos en el scope de la sesión como lo sugiere barnyr. Hemos sido quemados por usuarios que cambian inadvertidamente sus variables de sesión abriendo nuevas tabs del navegador.

Un enfoque que a menudo utilizo es crear un objeto de consulta de los campos del formulario y usar QofQ para decidir si necesito actualizar algo. Aquí hay un ejemplo simple.

  RecordsToUpdate = QueryNew("a"); FormValues = QueryNew("id,name","integer,varchar"); Delim = Chr(30);     not relevent here   ThisId = RemoveChars(ThisElement,1,17); AddNewRow(FormValues,"id#Delim#name", "#ThisID##Delim##ThisValue#", Delim); // AddNewRow is a udf     select FormValues.* from FormValues, GetCategories where id = CategoryId and name <> CategoryName   update query  

Por cierto, el uso de Chr (30) es algo que aprendí de Adam Cameron aquí mismo, en Stack Overflow.