Extender entidad User.php de Sonata User Bundle

En esta ocasión voy a explicar como extender la clase de Sonata User Bundle, User.php, en Symfony2.

Esta extensión es util cuando necesitamos añadir más campos a la entidad que se encarga de los usuarios.

Por seguir con el sistema de anotaciones que utilizo en el resto de entidades, lo primero es modificar el mapeo por defecto de la entidad User.php que se realiza mediante xml.

Para ello, nos aseguramos de tener las siguientes líneas en nuestro config.yml

doctrine:
    orm:
        auto_mapping: true

Ahora cambiamos el tipo de mapeo de nuestra entidad src/Application/Sonata/UserBundle/User.php que queda del siguiente modo:

<?php

/**
 * This file is part of the <name> project.
 *
 * (c) <yourname> <youremail>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Application\Sonata\UserBundle\Entity;

use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Doctrine\ORM\Mapping as ORM;

/**
 * This file has been generated by the Sonata EasyExtends bundle.
 *
 * @link https://sonata-project.org/bundles/easy-extends
 *
 * References :
 *   working with object : http://www.doctrine-project.org/projects/orm/2.0/docs/reference/working-with-objects/en
 *
 */
/**
 * User
 *
 * @ORM\Table(name="fos_user_user")
 * @ORM\Entity
 */
class User extends BaseUser
{
    
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * Get id
     *
     * @return integer $id
     */
    public function getId()
    {
        return $this->id;
    }
}

y por seguir el mismo sistema de mapeo a la entidad src/Application/Sonata/UserBundle/Group.php:

<?php

/**
 * This file is part of the <name> project.
 *
 * (c) <yourname> <youremail>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Application\Sonata\UserBundle\Entity;

use Sonata\UserBundle\Entity\BaseGroup as BaseGroup;
use Doctrine\ORM\Mapping as ORM;

/**
 * This file has been generated by the Sonata EasyExtends bundle ( http://sonata-project.org/bundles/easy-extends )
 *
 * References :
 *   working with object : http://www.doctrine-project.org/projects/orm/2.0/docs/reference/working-with-objects/en
 *
 */
/**
 * Group
 *
 * @ORM\Table(name="fos_user_group")
 * @ORM\Entity
 */
class Group extends BaseGroup
{
    
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * Get id
     *
     * @return integer $id
     */
    public function getId()
    {
        return $this->id;
    }
}

El siguiente paso, es eliminar los XML que ya no estamos utilizando para el mapeo, y eliminamos la carpeta src\Application\Sonata\UserBundle\Resources\config\doctrine\ completa.

Hecho esto, vamos a extender la clase User.php.

Modificamos la entidad User.php, por ejemplo:

<?php

namespace Application\Sonata\UserBundle\Entity;

use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Sonata\UserBundle\Model\UserInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
// Validación
use Symfony\Component\Validator\Constraints as Assert;


/**
 * User
 *
 * @ORM\Table(name="fos_user_user")
 * @ORM\Entity
 * @UniqueEntity(
 *     fields={"email"},
 *     message="Este email ya está en uso por otro usuario, utiliza otro"
 * )
 * @UniqueEntity(
 *     fields={"username"},
 *     message="Este nombre de usuario ya está en uso, elije otro"
 * )
 */
class User extends BaseUser {

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var boolean
     *
     * @ORM\Column(name="nuevoCampo", type="boolean", nullable=true)
     *
     * @Assert\NotBlank()
     * @Assert\Length(
     *      max = 9,
     *      maxMessage = "El nuevoCampo debe tener {{ limit }} carácteres"
     * )
     */
    private $nuevoCampo;

    /**
     * Get id
     *
     * @return integer $id
     */
    public function getId() {
        return $this->id;
    }

    /**
     * Set nuevoCampo
     *
     * @param boolean $nuevoCampo
     * @return User
     */
    public function setNuevoCampo($nuevoCampo)
    {
        $this->nuevoCampo = $nuevoCampo;

        return $this;
    }

    /**
     * Get nuevoCampo
     *
     * @return boolean 
     */
    public function getNuevoCampo()
    {
        return $this->nuevoCampo;
    }

}

Como se puede ver, también he aprovechado para configurar algunas validaciones, como definir campos únicos el email y el username. Y el nuevo campo no puede estar en blanco y como mínimo debe tener 9 caracteres.

Solo nos queda crear el Admin Class para nuestra nueva entidad:

<?php

namespace Application\Sonata\UserBundle\Admin;

use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Route\RouteCollection;
use Sonata\UserBundle\Model\UserInterface;
use Sonata\UserBundle\Admin\Model\UserAdmin as SonataUserAdmin;

class UserAdmin extends SonataUserAdmin {

    // Fields to be shown on create/edit forms
    protected function configureFormFields(FormMapper $formMapper) {

        $formMapper
                ->with('Datos de acceso', array('class' => 'col-md-6 col-lg-3'))
                    ->add('enabled', null, array('required' => false))
                    ->add('username', null, array('label' => 'Usuario'))
                    ->add('plainPassword', 'text', array(
                        'required' => (!$this->getSubject() || is_null($this->getSubject()->getId()))
                    ))
                    ->add('groups', 'sonata_type_model', array(
                        'btn_add' => false,
                        'required' => false,
                        'expanded' => true,
                        'multiple' => true
                    ))
                ->end()
                ->with('Datos de usuario', array('class' => 'col-md-6 col-lg-3'))
                    ->add('email', null, array('label' => 'Email de contacto', 'required' => true))
                    ->add('phone', null, array('label' => 'Teléfono de contacto'))
                    ->add('firstname', null, array('label' => 'Nombre'))
                    ->add('lastname', null, array('label' => 'Apellidos'))
                ->end()
                ->with('Otros datos', array('class' => 'col-md-6 col-lg-3'))
                   ->add('gender', 'sonata_user_gender', array(
                        'label'    => 'Género',
                        'required' => true,
                        'translation_domain' => 'SonataUserBundle',
                        'choices' => array(
                            UserInterface::GENDER_FEMALE => 'gender_female',
                            UserInterface::GENDER_MALE   => 'gender_male',
                            UserInterface::GENDER_UNKNOWN   => 'gender_unknown',
                        )
                    ))
                    ->add('nuevoCampo')
                ->end()
            ;
    }

    // Fields to be shown on filter forms
    protected function configureDatagridFilters(DatagridMapper $datagridMapper) {
        $datagridMapper
                ->add('id')
                ->add('username')
                ->add('email')
                ->add('phone', null, array('label' => 'Teléfono'))
                ->add('locked')
                ->add('groups')
        ;
    }

    // Fields to be shown on lists
    protected function configureListFields(ListMapper $listMapper) {
        $listMapper
                ->addIdentifier('email', null, array('label' => 'Email', 'editable' => true))
                ->add('phone', null, array('label' => 'Telefono', 'editable' => true))
                ->add('username', null, array('label' => 'Usuario', 'editable' => true))
                ->add('nuevoCampo', null, array('editable' => false))
                ->add('enabled', null, array('editable' => true))
                ->add('locked', null, array('editable' => true))
        ;
    }
}

Indicamos que debe cargar este nueva Admin Class en config.yml:

sonata_user:
    admin:
      user:
        class: Application\Sonata\UserBundle\Admin\UserAdmin

Y por último modificamos el archivo security.yml:

security:
    encoders:
        Application\Sonata\UserBundle\Entity\User:
            algorithm: sha512
            encode_as_base64: true
            iterations: 2