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

Problema con los listeners y el evento postPersist al registrar un usuario

17 de marzo de 2015

Muy buenas,

He creado un listener con el evento postPersist para que a la hora del registro de un usuario FosUserBundle envíe un correo a su cuenta de email.

El problema viene cuando creo el usuario $this->userManager->updateUser($user), porque en este mismo instante después de crearlo obtengo la id del usuario, para guardar la foto de su perfil en una carpeta con su id.

¿Que problema tengo?

Pues que vuelvo actualizar el usuario con la ruta de la foto, pero el listener ya no contiene la ruta de la foto, porque ya es una actualización. Entonces en email que le envío al usuario no muestra su foto.

Entonces he intentado $this->em->getConnection()->lastInsertId(), pero nada: me muestra 0. Supongo porque no ha ocurrido ningún nuevo registro antes de intentar capturar el último id insertado, para crear la carpeta con dicho id.

También en el mismo listener.

$em = $this->container->get('doctrine');
$usernew = $em->getRepository('AcmeUserBundle:User')->find($user->getId());

Esto no lo entiendo: hago una consulta y me muestra la misma que la anterior sin la actualización. Si luego entro en el perfil del usuario, me muestra la imagen correctamente.

¿Qué soluciones tengo para poder mostrar la imagen en el correo sin tener que utilizar PostUpdate, sólo con PostPersist?


Respuestas

#1

El error que te sucede con lastInsertId() es normal, ya que es un método que hay que ejecutarlo en un orden adecuado o no funcionará como esperas: tienes que ejecutarlo justo después del flush() que completa el INSERT y antes que cualquier otra consulta. Así que no te aconsejo que te bases en ese método para la lógica de tu aplicación.

Con respecto al problema que comentas, lo que me confunde es que estás usando FOSUserBndle y hablas del evento postPersist de Doctrine. Creo que deberías usar en su lugar los propios eventos de FOSUserBundle. En concreto, hay un evento que se llama FosUserEvents:: REGISTRATION_COMPLETED y que se lanza cuando el usuario ya se ha guardado correctamente en la base de datos y antes de enviarle la respuesta de que se ha registrado bien. Si lo necesitas, echa un vistazo a este ejemplo de listener de FosUserBundle para saber cómo usarlo.

@javiereguiluz

17 marzo 2015, 8:32
#2

Gracias Javier,

El evento de Doctrine lo he hecho para hacer algún listener porque no había hecho ninguno hasta ahora. Igualmente creo que voy a tener mismo problema utilizando el evento de FosUserEvents:: REGISTRATION_COMPLETED, porque la imagen la consigo de facebook y el registro lo hago como el código del siguiente enlace Integrar FosUserBundle con HWIOAuthBundle.

La imagen de Facebook la obtengo con la siguiente url https://graph.facebook.com/".$username_id."/picture?type=large y no obtengo un objeto. Entonces para subir la foto a través de las funciones de la entidad me pide que sea un objeto UploadedFile. Entonces es aquí mi pregunta que realmente no si es la idónea o correcta. ¿Se puede convertir a un objeto UploadedFile con la URL de la imagen, para el siguiente código que asigna el objeto?

/**
     * Set foto.
     *
     * @param UploadedFile $foto
     */
    public function setFoto(UploadedFile $foto = null)
    {
 
        $this->foto = $foto;
        // check if we have an old image path
        if (isset($this->rutaFoto)) {
            // store the old name to delete after the update
            $this->temp = $this->rutaFoto;
            $this->rutaFoto = null;
        }else{
 
            $nombreSlug=Util::getSlug($this->getUsername());
            $nombre = "avatar-".$nombreSlug.".".$this->getFoto()->guessExtension();
            $this->rutaFoto=$nombre;
        }
        return $this;
 
    }

Me estoy perdiendo un poco en esto y ya no encuentro más salida.

Espero que puedas ayudarme Javier.

Un gran abrazo.

@jcarlosweb

17 marzo 2015, 18:41
#3

Este sitio web que estás visitando está hecho con Symfony y usa HWIOAuthBundle. La diferencia es que yo uso Twitter en vez de Facebook, no guardo el archivo de la imagen sino su ruta y que no uso FOSUserBundle. Aún así, creo que podrías hacer como yo y persistir el usuario dentro del método loadUserByOAuthUserResponse().

Cuando el usuario no existe, lo persisto dentro de ese método y luego devuelvo el objeto del usuario. En el código que has enlazado se devuelve el usuario sin persistirlo antes. Si lo haces como te indico, ya tendrías el usuario creado y por tanto, ya podrías acceder a su id.

@javiereguiluz

17 marzo 2015, 19:39
#4

He intentado hacer lo que me dices, pero no consigo persistir o realmente no se hacerlo.

Pongo el código de la subida de foto. Aquí he agregado el postPersist:

/**
 * Set foto.
 *
 * @param UploadedFile $foto
 * @ORM\PostPersist()
 */
 
public function setFoto(UploadedFile $foto = null)
{
 
    $this->foto = $foto;
    // check if we have an old image path
    if (isset($this->rutaFoto)) {
        // store the old name to delete after the update
        $this->temp = $this->rutaFoto;
        $this->rutaFoto = null;
    } else {
        $nombreUser=Util::getSlug($this->getUsername());
        $nombre = "avatar-".$nombreUser.".".$this->getFoto()->guessExtension();
        $this->rutaFoto=$nombre;
    }
 
    return $this;
}
/**
 * Get rutaFoto
 *
 * @return string
 */
public function getRutaFoto()
{
    if (!$this->rutaFoto)
        return "images/".$this->getGender().".jpg";
    else
       return "/fotos/users/".$this->getId()."/".$this->rutaFoto;
}
Catchable Fatal Error: Argument 1 passed to Acme\UserBundle\Entity\User::setFoto()
must be an instance of Symfony\Component\HttpFoundation\File\UploadedFile,
instance of Doctrine\ORM\Event\LifecycleEventArgs given, called in
/var/www/myproject/vendor/doctrine/orm/lib/Doctrine/ORM/Event/ListenersInvoker.php on 
line 102 and defined

@jcarlosweb

17 marzo 2015, 22:03
#5

Buenos días Javier,

He estado buscando y no encuentro la manera de hacerlo como me comentas. La única manera que se me ocurre es crear mi propio evento y ejecutarlo con event dispatcher.

¿Pero qué otras opciones tengo?

Saludos.

@jcarlosweb

19 marzo 2015, 9:07
#6

El error que se te produce es porque estás mezclando las dos formas de manejar la subida de archivos que se explican en este artículo de la documentación oficial de Symfony. Al método setFoto() no puedes pasarle el argumento $foto de tipo UploadedFile.

Por otra parte, aunque en teoría lo que quieres hacer no parece tan complejo, te indico dos soluciones alternativas radicalmente diferentes:

  • La primera alternativa puede parecer absurda, pero sería no incluir la foto del usuario en el email que envías después del registro. Me he registrado en miles de sitios y servicios y nunca he visto mi foto en el email que me envían para confirmar el registro. Quizás te estás complicando en exceso sin motivo.
  • La segunda alternativa sería diseñar un comando muy sencillo que se ejecute cada minuto y envíe automáticamente el email de registro de usuario. Tu aplicación sólo se dedica a registrar usuarios, guardar sus fotos y demás ... y el comando por separado se encarga de preparar el mensaje, coger la foto y enviar el email.

@javiereguiluz

19 marzo 2015, 9:38
#7

Muchas gracias Javier,

La primera alternativa es la que tenía en mente.

Sobre la segunda opción, mi pregunta es: ¿el comando se puede ejecutar directamente desde PHP, tiene que ser con tareas Cron job o desde la consola directamente?

Otra cosa Javier, ¿qué te parece la opción que te he dicho antes de crear mi propio evento y lanzarlo con event dispatcher?

Gracias por todo.

@jcarlosweb

19 marzo 2015, 12:47
#8

Creo que lo más correcto sería programar una tarea en el cron que ejecute el comando Symfony. Y como desde los comandos puedes acceder muy fácil a la base de datos y a cualquier servicio de la aplicación, puedes hacer prácticamente lo que quieras.

Respecto a crear tu propio evento, en teoría me parece bien y es algo que hace normalmente cualquier aplicación Symfony que no sea trivial. Pero como no uso FOSUserBundle, no tengo criterio para decirte si sería mejor y más fácil aprovechar alguno de los eventos que te ofrece ese bundle. Pero bueno, lo que es seguro es que crear tu evento es correcto y te va a funcionar bien.

@javiereguiluz

19 marzo 2015, 14:46