Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Guardar datos temporales

30 de octubre de 2013

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

#1

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

31 octubre 2013, 10:03
#2

¡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

31 octubre 2013, 11:27
#3

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

31 octubre 2013, 11:51
#4

Voy a intentarlo por ahí entonces, gracias por tu ayuda, has sido de mucha ayuda en estas dudas.

Un saludo Javier

@Jorge_Gante

31 octubre 2013, 15:56