msgbartop
Desarrollador Web, Android y iOS
msgbarbottom

Loguear un usuario manualmente en Symfony2

Es posible que queramos loguear automaticamente a un usuario en Symfony2 sin que haga falta que introduzca su email y contraseña. El caso típico es cuando un usuario se acaba de registrar en nuestra web y queremos loguearle en el sistema sin que tenga que volver a introducir sus datos.

En versiones anteriores a Symfony 2.3, este código funcionaba correctamente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// security.yml
providers:
    main:
        entity: { class: Application\Bundle\DefaultBundle\Entity\User, property: username }
 
firewalls:  
    secured_area:
        pattern:    ^/
        form_login:
            check_path: /login_check
            login_path: /login
        logout:
            path:   /logout
            target: /
        anonymous: ~

Y en nuestro código teníamos lo siguiente:

1
2
3
4
$user = $em->getRepository('ApplicationDefaultBundle:User')->findOneBy(array('id' => 117));
$provider_name = "main";
$token = new UsernamePasswordToken($user, null, $provider_name, $user->getRoles());
$this->get('security.context')->setToken($token);

A partir de Symfony 2.3, esto ha cambiado un poco. En el código anterior vemos que a la clase UsernamePasswordToken le pasamos el nombre del provider como tercer parámetro.

En Symfony2 >= 2.3 tenemos que pasar el nombre del firewall y en el security.yml, especificar que provider utiliza ese firewall.

Lo vemos a continuación:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// security.yml
providers:
    main:
        entity: { class: Application\Bundle\DefaultBundle\Entity\User, property: username }
 
firewalls:  
    secured_area:
        pattern:    ^/
        provider: main # añadimos el nombre del provider a utilizar
        form_login:
            check_path: /login_check
            login_path: /login
        logout:
            path:   /logout
            target: /
        anonymous: ~

Y el código lo modificaremos de la siguiente forma:

1
2
3
4
$user = $em->getRepository('ApplicationDefaultBundle:User')->findOneBy(array('id' => 117));
$firewall_name = "secured_area"; // Ahora utilizamos el nombre del firewall
$token = new UsernamePasswordToken($user, null, $firewall_name, $user->getRoles());
$this->get('security.context')->setToken($token);

Espero que os sirva de ayuda :)

Etiquetas: , , , ,

Añadir el atributo placeholder de html5 en un formulario de Twig y Symfony2

La llegada de HTML5 ha puesto a nuestra disposición nuevos elementos y atributos para añadir en nuestras webs. Uno de ellos es el placeholder que, para resumirlo, es el texto que aparece dentro de un campo de texto antes de que escribamos algo. Es muy útil para indicar al usuario que es lo que queremos que introduzca en ese campo. Es algo parecido a los labels, pero dentro del propio input.

Puntualizar que la W3C recomienda siempre utilizar labels junto a los inputs y que el placeholder no reemplaza al label.

¿Cómo añadir el atributo placeholder a un formulario de Twig en Symfony2?

Vamos a partir de un formulario sencillo:

<form action="" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form.username) }}
</form>

Si queremos añadir el atributo placeholder lo haremos de la siguiente manera:

<form action="" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form.username, { 'attr': {'placeholder': 'Escribe tu nombre de usuario' } }) }}
</form>

Si además queremos que el placeholder sea traducible, solo deberemos utilizar el filtro trans de Twig:

<form action="" method="post" {{ form_enctype(form) }}>
    {{ form_widget(form.username, { 'attr': {'placeholder': 'Escribe tu nombre de usuario' | trans } }) }}
</form>

Etiquetas: , , , , , , ,

Convertir Eclipse en un buen IDE para programar con Symfony2

El IDE que utilizamos para programar es algo bastante personal pero lo cierto es que un buen entorno de desarrollo puede ayudarnos en nuestro día a día a ser más productivos y a programar con mayor rapidez. Para PHP podemos utilizar diferentes IDEs como pueden ser NetBeans, Sublime Text 2, PHPStorm, etc.

Eclipse suele ser más utilizado para lenguajes de programación tipo JAVA, pero lo cierto es que es un IDE bastante potente y se puede adaptar a cualquier lenguaje de programación gracias a la gran cantidad de plugins que hay disponibles. En este post voy a explicar como configurar Eclipse para utilizarlo cuando estemos desarrollando con Symfony2.

Evidentemente, lo primero que debemos hacer es descargarnos el IDE desde la propia web oficial de Eclipse. En este caso vamos a utilizar la versión Eclipse Classic (en este momento, versión 4.2).

Ahora vamos a descargarnos el plugin creado por Robert Gründler para ayudarnos en la programación con Symfony2. Algunas de las ventajas que obtendremos con este plugin son:

- Asistente de código para elementos específicos de Symfony2, como los servicios, rutas, entities, traducciones, bloques de Twig, etc.
- Navegación: Con un click podremos ir rapidamente a rutas, plantillas, bloques/funciones/filtros de Twig, servicios, etc.
- Soporte para anotaciones
- Soporte para Twig

Por si esto fuera poco, se incluyen plugins de terceros que nos seguirán facilitando el trabajo:
- Plugin para editar archivos Yaml.
- Asistente de código para Doctrine.
- Generador de setters/getters.
- Plugin de composer

Este plugin lo podemos encontrar en http://symfony.dubture.com/. La forma más fácil de instalarlo es desde el propio Eclipse, por lo que vamos a Help > Install new software y pulsamos sobre el botón “Add”. En name escribimos “Symfony2 Eclipse Plugin” y en location escribimos esta url: http://p2.dubture.com.

Seleccionamos todos los paquetes que aparecen y los instalamos.

Algo que también nos puede resultar de mucha ayuda es tener una consola directamente integrada en Eclipse. Esto lo podemos conseguir con el plugin Wickedshell. Para instalarlo lo haremos de la manera habitual: Help > Install new software, pulsamos el botón “Add” y como nombre de escribimos “Consola Wickedshell” y como url http://www.wickedshell.net/updatesite.

Cuando lo instalemos, tendremos una consola integrada en el propio Eclipse:

Por último y no menos importante, es cambiar la apariencia de Eclipse e instalar un theme con el que nos encontremos más a gusto programando. Para ello vamos a instalar el plugin que encontraremos en http://eclipsecolorthemes.org/ con varios themes ya disponibles para su utilización.

Lo instalamos como en pasos anteriores: Help > Install new software, pulsamos el botón “Add” y como nombre de escribimos “Eclipse Color Themes” y como url http://eclipse-color-theme.github.com/update/.

Ya solo nos queda seleccionar el theme que mejor se adapte a nosotros:

Etiquetas: , , , , , , , ,

Desplegable con una relación entre entities en un formulario de Symfony2

En las entidades de Symfony2 es sencillo relacionar un campo con otra tabla con una relación ManyToOne y que este campo solo pueda tomar un valor que esté disponible en la segunda entity. Si creamos un formulario para poder introducir nuevos registros en la base de datos, necesitamos un campo del tipo select para seleccionar uno de los datos de la segunda tabla y relacionarlos.

Deberemos modificar el formulario y añadirle un par de opciones más. En este ejemplo, vamos a relacionar un usuario con su país.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace Acme\MiBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
 
class PerfilType extends AbstractType {
 
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('pais', 'entity',
            array(
                'class' => 'AcmeMiBundle:Pais'
                'label' => 'Pais',
            )
        );
    }
}

Que no se nos olvide añadir el método __toString() a la entity Pais:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Acme\MiBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
class Pais
{
 
    [...]
 
    public function __toString()
    {
        return $this->getNombre();
    }
}

Y así de sencillo, ahora en nuestra plantilla aparecerá automáticamente el campo de tipo select a la hora de seleccionar el país del usuario.

Etiquetas: , , , ,

Error con Symfony2: You cannot refresh a user from the EntityUserProvider that does not contain an identifier

He creado dos bundles, uno llamado UserBundle y otro ProfileBundle. El bundle UserBundle es totalmente independiente y reutilizable en cualquier proyecto (contando con funcionalidades tales como registro, login, logout, recordar contraseña, confirmar usuario mediante email, etc.) con la única pega de que solamente se guardan el email y la contraseña del usuario. Hay proyectos que solo requieren estos datos y no merece la pena “ensuciarlo” con más.

El bundle ProfileBundle subsana esta limitación y otorga al usuario un perfil con su nombre, apellidos, etc. El registro se hace desde el UserBundle y, una vez logueado en la aplicación, completas tu perfil de usuario mediante el ProfileBundle.

Al terminar de completar tu perfil y pulsar el botón guardar, el usuario logueado se recarga con los nuevos datos introducidos. Al hacer esto, me aparecía el siguiente error:

You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine.

Y en otras ocasiones también se mostraba este otro error:

Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken::serialize() must return a string or NULL

La solución es sencilla y solo hay que fijarse en el mensaje de error. Si el usuario se va a recargar una vez ya está logueado, la entidad Usuario debe implementar la clase Serializable y definir sus métodos serialize() y unserialize().

En diferentes blogs que hablan sobre Symfony2, en los métodos serialize y unserialize hacen uso del método getUsername() lo cual puede ser un error. Lo muestro con un ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class User implements UserInterface, \Serializable
{
 
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /**
     * @var string $email
     *
     * @ORM\Column(name="email", type="string", length=255, unique=true)
     * @Assert\Email()
     */
    private $email;
 
    [...]
 
    function getUsername()
    {
        return $this->getEmail();
    }
 
    public function serialize()
    {
       return serialize($this->getUsername());
    }
 
    public function unserialize($data)
    {
        $this->email = unserialize($data);
    }
}

En el ejemplo anterior vemos que la propiedad $email no es la clave primaria y, aunque hayamos marcado que es única, nos seguirá apareciendo el error anterior ya que Doctrine necesita que el refresh del usuario se haga mediante su identificador único (en algún caso podría llegar a ser el email, pero no en este ejemplo).

Solamente tendremos que cambiar el código anterior por este:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class User implements UserInterface, \Serializable
{
 
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /**
     * @var string $email
     *
     * @ORM\Column(name="email", type="string", length=255, unique=true)
     * @Assert\Email()
     */
    private $email;
 
    [...]
 
    function getUsername()
    {
        return $this->getEmail();
    }
 
    public function serialize()
    {
       return serialize($this->getId());
    }
 
    public function unserialize($data)
    {
        $this->id = unserialize($data);
    }
}

El código anterior se puede optimizar y se podrían añadir más propiedades a los métodos serialize y unserialize (se podrían utilizar en conjunto las propiedades id, email, etc.).

Etiquetas: , , , , , , , ,

Symfony2: Añadir a un formulario un campo que no está definido en el modelo

He estado desarrollando mi propio bundle para el registro de usuarios en Symfony2 y me he encontrado con el problema de querer añadir campos al formulario de registro que no he definido en el modelo. Uno de estos campos es un checkbox para aceptar los términos de uso de la aplicación. No me interesa guardar este campo en la base de datos por lo que no está definido en el modelo.

Así es como lo he hecho:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class RegisterFormType extends AbstractType
{
 
    public function buildForm(FormBuilder $builder, array $options)
    {
 
        $builder
            ->add('email', 'email')
            ->add('password', 'repeated', array(
                'type' => 'password',
                'invalid_message' => 'Las dos contraseñas deben coincidir',
                'options' => array('label' => 'Contraseña'),
                'required' => false))
	    ->add("accept_tos",
                "checkbox", array(
                    "property_path" => false,
                )
            );
 
 
        $builder
            ->addValidator(new CallbackValidator(function(FormInterface $form){
 
                if (!$form["accept_tos"]->getData())
                {
                    $form->addError(new FormError('Debes aceptar los términos de uso'));
                }
            })
        );
    }
 
}

Como vemos, añadimos un campo checkbox con nombre “accept_tos” y le indicamos la opción “property_path” a false, por lo que no validará este campo con el modelo. Eso sí, debemos añadir un validador para este campo para asegurarnos de que el usuario acepta las condiciones de uso.

Fuente: http://www.richsage.co.uk/2011/07/20/adding-non-entity-fields-to-your-symfony2-forms/

Etiquetas: , , , , , ,

Desloguear (logout) a un usuario mediante programación en Symfony2

¿Cómo se puede desloguear un usuario sin tener que acceder a la ruta correspondiente del logout? Para algunas funcionalidades que queramos añadir a nuestro proyecto, es posible que necesitemos desloguear al usuario. Un ejemplo podría ser el caso de desactivar/borrar su cuenta, pero puede que haya más (todo depende de nuestra imaginación ;) ). Vamos a ver como hacerlo con Symfony2.

$this->get("request")->getSession()->invalidate();
$this->get("security.context")->setToken(null);

Como extra, podemos informar al usuario mediante un mensaje flash y le redirigimos a la página que queramos:

$this->get("session")->setFlash('message.success', true);
return new RedirectResponse($this->generateUrl('homepage'));

Evidentemente, la página a la que vamos a redirigir tiene que poder ser vista por usuarios anónimos (no debe necesitar autentificación).

Pd.: Ya escribí como Instalar Symfony2 desde cero

Etiquetas: , , , ,

Integrar Sphinx en Symfony2 con Doctrine2

En el grupo de Symfony2 hay una gran cantidad de preguntas entre las que, de vez en cuando, podemos encontrar un buen hilo donde se trata un tema interesante. Entre tanta “paja” (preguntas fáciles de responder solamente leyendo la documentación) es posible que pasemos por alto alguna de estas buenas preguntas junto a sus respuestas.

Voy a intentar recopilar este tipo de hilos y traducirlos al castellano, siempre dejando el enlace al hilo original del grupo de Symfony2 por si hay alguna nueva respuesta.

Para empezar, podemos encontrar como utilizar Sphinx haciendo uso de la api como un servicio en Symfony2.

1) Nos descargamos la api de Sphinx de la página oficial http://sphinxsearch.com/, colocándolo en la carpeta vendor/sphinx-client. Debemos renombrar la clase a SphinxClient.

2) Lo añadimos en el autoload.php

  $loader->registerPrefixes(array(
   // ...
   'Sphinx'           => __DIR__.'/../vendor/sphinx-client',
  ));

3) Añadimos Sphinx como servicio de Symfony2 en el fichero config.yml

 services:
     search:
         class: SphinxClient
         calls:
             - [setArrayResult, [true]]
             - [setLimits, [0, 20, 1000]]

4) Por último, podemos hacer uso del servicio desde el controlador de esta forma:

$search  = $this->get('search');
$results = $search->Query($search_terms, 'myindex');

Evidentemente deberemos leer la documentación de Sphinx para instarlo y configurarlo correctamente. La página oficial es bastante completa en ese sentido.

Hilo original: doctrine2 and sphinx

Etiquetas: , , , , ,

Instalar Symfony2 desde cero

Nos vamos acercando inevitablemente al cambio de Symfony 1.x a Symfony2 y la conclusión que he sacado de mi asistencia al evento #desymfony es que cuanto antes nos pongamos con ello, mucho mejor. Symfony2 mejora a su predecesor en numerosos aspectos y se convertirá en el framework definitivo para el desarrollo de aplicaciones web en el futuro. Si quieres saber todas las nuevas características de esta versión, te recomiendo que visites la web oficial de Symfony2: http://symfony.com/.

En este primer post procederemos a su instalación. Personalmente, me gusta programar en local (en mi propio ordenador) y una vez haya acabado con el desarrollo o quiera probarlo en un entorno de producción, hacer el deploy a un servidor real. Por ello, primero voy a explicar como configurar un servidor web en nuestro ordenador y después procederemos con la instalación de Symfony2.

La instalación la voy a hacer en un entorno Linux con la distribución Ubuntu 11.04. Adaptar los comandos según nuestra distribución (o sistema operativo).

1) Primero instalamos Apache

# apt-get install apache2 apache2.2-common apache2-utils ssl-cert  apache2-mpm-prefork

2) Instalamos y habilitamos el módulo para utilizar modrewrite

# apt-get install libapache2-mod-proxy-html

Para habilitar el modulo en apache, ejecutamos:

# a2enmod rewrite

3) Instalamos PHP5

# apt-get install php5 libapache2-mod-php5 php5-common php5-intl

4) Instalamos Mysql

# apt-get install mysql-server mysql-client php5-mysql

5) Por último instalamos phpmyadmin (opcional)

# apt-get install phpmyadmin

Con estos sencillos pasos ya tendremos un servidor web instalado en nuestro ordenador.

Ahora continuamos con la instalación de Symfony2. Mi IDE favorito para programar es Netbeans, por lo que es el que utilizaré en este ejemplo. Si no lo hemos cambiado, nuestro workspace (donde se encuentran los proyectos a los que podemos acceder desde Netbeans) lo encontraremos en la carpeta NetBeansProjects en nuestra carpeta Home.

Procedemos a crear una nueva carpeta con el nombre de nuestro proyecto en el workspace de Netbeans:

$ mkdir $HOME/NetBeansProjects/miproyecto

Entramos en la carpeta que acabamos de crear:

$ cd $HOME/NetBeansProjects/miproyecto

Descargamos Symfony2 (comprueba la última versión en http://symfony.com/download):

$ wget http://symfony.com/download?v=Symfony_Standard_Vendors_2.0.0-RC4.tgz

Y descomprimimos:

$ tar zxvf Symfony_Standard_Vendors_2.0.0-RC4.tgz

Borramos el archivo comprimido que hemos descargado:

$ rm Symfony_Standard_Vendors_2.0.0-RC4.tgz

Ya tenemos instalado un proyecto básico en Symfony2. Ahora vamos a configurar Apache para poder acceder desde nuestro navegador favorito.

Creamos un nuevo archivo donde irá la configuración de nuestra web:

# vi /etc/apache2/sites-available/miproyecto

Que contendrá el siguiente contenido (cambia “jonseg” por el nombre de tu carpeta Home):

<VirtualHost *:80>
   ServerAdmin email@email.com
   DocumentRoot /home/jonseg/NetBeansProjects/miproyecto/Symfony/web
   ServerName miproyecto.com
   ServerAlias *.miproyecto.com
   ErrorLog /var/log/apache2/miproyecto_error.log
   CustomLog /var/log/apache2/miproyecto_access.log common
   DirectoryIndex index.php
   <Directory "/home/jonseg/NetBeansProjects/miproyecto/Symfony/web">
        Options Indexes FollowSymLinks
        Order Allow,Deny
        Allow from all
        AllowOverride all
        <IfModule mod_php5.c>
           php_admin_flag engine on
           php_admin_flag safe_mode off
           php_admin_value open_basedir none
        </ifModule>
   </Directory>
</VirtualHost>

Creamos un enlace simbólico de este archivo en sites-enabled para indicar que es una web activa:

# ln -s /etc/apache2/sites-available/miproyecto /etc/apache2/sites-enabled/miproyecto

Para que se apliquen los cambios, reiniciamos el servidor Apache:

# /etc/init.d/apache2 restart

Por último, forzamos en el archivo /etc/hosts la ip de nuestra web, que en este caso será localhost o 127.0.0.1

# vim /etc/hosts

Y añadimos esta línea al final del fichero:

127.0.0.1 www.miproyecto.com miproyecto.com

Después de todo esto, ya podremos acceder a nuestro nuevo proyecto en Symfony2 desde el navegador escribiendo la url que hemos configurado.

Como primer paso, debemos comprobar si cumplimos con la configuración básica para que funcione nuestro proyecto. Lo haremos desde la siguiente url:

http://miproyecto.com/config.php

Symfony2 config

Como vemos, nos encontramos con dos problemas (podríamos tener más, deberemos resolverlos según sea necesario). En este caso el error es que las carpetas app/cache y app/logs no tienen los permisos necesarios.

Lo arreglaremos así:

chmod 777 $HOME/NetBeansProjects/miproyecto/Symfony/app/cache
chmod 777 $HOME/NetBeansProjects/miproyecto/Symfony/app/logs

Una vez arreglados los problemas, ya podemos acceder a nuestro proyecto web con Symfony2 mediante esta url:

http://miproyecto.com/app_dev.php/

Symfony2 home

En próximos artículos nos iremos adentrando poco a poco en el desarrollo de Bundles, las plantillas con Twig, etc.

Cualquier duda, en los comentarios!

Etiquetas: , , , , ,