Muy buenas,
Tras seguir el libro de Javier Eguiluz, "Desarrollo ágil Symfony 2.1" estoy desarrollando una página web con Symfony2 para que un ESTUDIANTE
(en mayúscula las entidades) pueda resolver un SUPUESTO
(= ejercicio). Para lo anterior, el ESTUDIANTE
realizará una PRACTICA
en la que deberá dar tantas RESPUESTAS
(= rellenar un form para cada RESPUESTA
) que se irán añadiendo a la PRÁCTICA
, o lo que es lo mismo, se irán acumulando objetos de tipo RESPUESTA
cada vez que el ESTUDIANTE
pulse sobre el botón submit que se relacionarán a un objeto de tipo PRÁCTICA
(parecido al típico carrito de la compra).
Cuando se finalice de dar RESPUESTAS
, se persistirán todas las RESPUESTAS
en su tabla, y lo mismo con el objeto PRÁCTICA
. Mi pregunta tiene que ver, por tanto, con guardar datos temporalmente.
Almaceno todos los datos con el propósito de que un PROFESOR
pueda ver las prácticas de un ESTUDIANTE
y pueda evaluarle. He desechado la idea de ir persistiendo los datos temporales en las tablas, porque crearía mucha "basura" si el ESTUDIANTE
cierra sin finalizar su PRACTICA
.
Entonces, mis preguntas son:
Primera pregunta ¿Dónde es mejor que almacene temporalmente las RESPUESTAS
y la PRACTICA
para persistirlos en la BD cuando el ESTUDIANTE
termine pulsando el botón "Finalizar Ejercicio" (que controlaré con AJAX)? Tras pensarlo mucho creo que las mejores opciones son:
1) Almacenarlo en la sesión del ESTUDIANTE
: se va machacando el objeto PRACTICA
guardado cada vez que se le añade una RESPUESTA
. La primera vez que se entra en la plantilla principal se hace algo tal que así:
$peticion = $this->getRequest(); $practica = $peticion->getSession()->get('practica'); if ($practica == null) { $practica = new Practica(); //Se asignan el resto de variables de $practica y... $peticion->getSession()->set('practica', $practica); } // Vista principal de la página $respuesta = $this->render('PprsBundle:Default:principal.html.twig', array( 'ejercicio' => $ejercicio ));
Y luego, conforme el ESTUDIANTE
va completando los campos del formulario y pulsando el botón submit:
/** * @Route("/pprs/principal/ejercicio={numero_ejercicio}", name="configcal") * @Template("PprsBundle:Default:Formquequierovisualizar.html.twig") */ public function FormquequierovisualizarAction($numero_ejercicio = null) { $peticion = $this->getRequest(); $respuesta = new Respuesta(); /******** Aqui va codigo que no es importante *********/ $form = $this->createForm(new RespuestaType(), $respuesta, array( 'select1' => $datosDefecto1, 'select2' => $datosDefecto2, 'select3' => $datosDefecto3, 'select4' => $datosDefecto4, )); // De esta forma compruebo que el submit POST proviene del formulario // calificacion_form if ($peticion->isMethod('POST') && $peticion->request->has('calificacion_form')) { // Tanto handleRequest como submit no pueden usarse tal y como // indican los tutoriales por ser añadidos a partir de Symfony 2.3 $form->bind($peticion); if ($form->isValid()) { // se supone que $respuesta se ha rellenado de los datos // escogidos en el form por el ESTUDIANTE; luego se relaciona // con la practica de la sesión $sesion = $peticion->getSession(); $practica = $sesion->get('practica'); $respuesta->setPractica($practica); // se "machaca" el valor anterior de $practica $sesion->set('practica', $practica); return $this->redirect($this->generateUrl('pprs_principal', array( 'numero_ejercicio' => $numero_ejercicio ))); } } // paso el objeto practica a la vista donde voy a mostrar el usuario // las respuestas que ya ha escogido return array( 'form' => $form->createView(), 'practica' => $practica, ); }
2) Recurriendo al paso de parámetros a través de los Action
de los controladores y los views
a través de redirect (pasando el objeto PRACTICA
modificado cada vez que el usuario añade una nueva RESPUESTA
, ¿se puede hacer esto?)
Pregunta 2 ¿Cual es la mejor opción?
Pregunta 3 En caso de que sea mejor la sesión, ¿me pueden poner un ejemplo sencillo de como darle uso? ¿En qué momento debo crear la sesión? ¿Tiene algo que ver con serializar los datos?
Bueno muchas gracias al que se tome el tiempo al menos de leer mi caso. Un saludo
Respuestas
En mi opinión, la primera opción que planteas (guardar los datos en la sesión) es mucho mejor que la otra opción de ir arrastrando los parámetros a través de controladores y plantillas (ni siquiera se si se podría hacer completamente de esa manera).
Los problemas que te puedes encontrar con esta solución son los habituales con las sesiones. Si tu aplicación está instalada en varios servidores o si utilizas aplicaciones como Varnish para mejorar el rendimiento, la gestión de las sesiones no es trivial.
Además, si la aplicación tiene muchos usuarios concurrentes y utilizas las habituales sesiones basadas en archivos, puede que el rendimiento se resienta. En ese caso, podrías optar por un almacenamiento en memoria con Redis o similar y persistir a base de datos sólo cuando el alumno complete la práctica.
Respecto a las otras preguntas que planteas, en Symfony2 no hace falta iniciar la sesión explícitamente, ya que el framework siempre la inicia cuando es necesario (por ejemplo al intentar leer o guardar información en la sesión). Esto es cierto a partir de Symfony 2.1, tal y como se puede leer en su changelog:
Removed 'auto_start' configuration parameter from session config. The session will start on demand.
Por otra parte, en efecto la sesión tiene que ver mucho con la serialización de datos. Aquí de nuevo Symfony2 hace un gran trabajo, ya que la serialización y deserialización de datos es completamente transparente. Puedes guardar en la sesión cualquier objeto complejo sin preocuparte.
Si lees la documentación PHPdoc del método loadSession
de la clase NativeSessionStorage
, verás lo siguiente:
After starting the session, PHP retrieves the session from whatever handlers are set to [...] PHP takes the return value from the read() handler, unserializes it and populates $_SESSION with the result automatically.
@javiereguiluz
¡Gracias por contestar Javier!
Me sorprende que Symfony no tenga algún método que haga esto más sencillo, sobre todo para aquellos desarrolladores de comercios electrónicos (aunque parece existir un bundle para ellos).
Lo intentaré con las sesiones (no uso Varnish), si bien en algún foro me comentaron la posibilidad de crear otra entidad "temporal" en la cual guarde todas las RESPUESTAS temporales, y posteriormente mediante alguna rutina automática, limpiar la tabla asociada periódicamente. Si tienes alguna experiencia con ese tipo de rutinas (por ejemplo una que se active cuando el usuario salga de la web), te rogaría que me guiaras de alguna forma porque esta solución puede ser aparentemente más sencilla, menos problemática y más elegante y eficiente.
¡Un saludo crack!
@Jorge_Gante
No entiendo cómo puede ser "más sencillo, elegante y menos problemático" crear entidades temporales intermedias que luego se convierten en definitivas y a las que se añaden tareas programadas que van limpiando las entidades temporales que ya no se usan.
Las sesiones se inventaron precisamente para guardar información del usuario entre diferentes peticiones HTTP, así que parece una solución adecuada para el caso que planteas. Evidentemente, para encontrar la solución ideal habría que valorar otras cosas, como por ejemplo el número de usuarios concurrentes, la complejidad de las prácticas (el número de ejercicios que contiene cada una) y las veces que las prácticas se dejan sin completar.
@javiereguiluz
Voy a intentarlo por ahí entonces, gracias por tu ayuda, has sido de mucha ayuda en estas dudas.
Un saludo Javier
@Jorge_Gante