Symfony2 FOS User Bundle

Symfony2 FOS User Bundle

FOS User Bundle provides a flexible framework for user management that aims to handle common tasks such as user registration and password retrieval.
Features include:

  • Users can be stored via Doctrine ORM
  • Registration support, with an optional confirmation per mail
  • Password reset support

Installation

Installation is a quick 7 step process:

  1. Download FOSUserBundle using composer
  2. Enable the Bundle
  3. Create your User class
  4. Configure your application's security.yml
  5. Configure the FOSUserBundle
  6. Import FOSUserBundle routing
  7. Update your database schema

Step 1: Download FOSUserBundle using composer

Add FOSUserBundle in your composer.json:

{
   "require": {
    "friendsofsymfony/user-bundle": "~2.0@dev"
   }
 }

Now tell composer to download the bundle by running the command:

>composer update friendsofsymfony/user-bundle

Composer will install the bundle to your project's vendor/friendsofsymfony directory.

Step 2: Enable the bundle

Enable the bundle in the kernel:

// app/AppKernel.php
   public function registerBundles()
   {
      $bundles = array(
      // ...
      new FOS\UserBundle\FOSUserBundle(),
      );
   }

Step 3: Create your User class

Create the User class for your application. The main goal of this bundle is to persist this User class to a database.

The bundle provides base classes which are already mapped for most fields to make it easier to create your entity. Here is how you use it:

  1. Extend the base User class (from the Model folder if you are using any of the doctrine variants)
  2. Map the id field. It must be protected as it is inherited from the parent class.
namespace Jigar\UserBundle\Entity;

use FOS\UserBundle\Model\User as BaseUser;

use Doctrine\ORM\Mapping as ORM;

/**

 * @ORM\Entity

 * @ORM\Table(name="fos_user")

 */

class User extends BaseUser

{

    /**

     * @ORM\Id

     * @ORM\Column(type="integer")

     * @ORM\GeneratedValue(strategy="AUTO")

     */

    protected $id;



    public function __construct()

    {

        parent::__construct();

        // your own logic

    }

}

Step 4: Configure your application's security.yml

Below is a minimal example of the configuration necessary to use the FOSUserBundle in your application:

# app/config/security.yml

security:

encoders:

FOS\UserBundle\Model\UserInterface: sha512 

    role_hierarchy:

        ROLE_ADMIN:       ROLE_USER

        ROLE_SUPER_ADMIN: ROLE_ADMIN

    providers:

        fos_userbundle:

            id: fos_user.user_provider.username

    firewalls:

        main:

            pattern: ^/

            form_login:

                provider: fos_userbundle

                csrf_provider: form.csrf_provider

            logout:       true

            anonymous:    true

    access_control:

        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }

        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }

        - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }

        - { path: ^/admin/, role: ROLE_ADMIN }

Step 5: Configure the FOSUserBundle

Add the following configuration to your config.yml file.

# app/config/config.yml

fos_user:
    db_driver: orm
    firewall_name: main
    user_class: Jigar\UserBundle\Entity\User

Step 6: Import FOSUserBundle routing files

By importing the routing files you will have readymade pages for things such as logging in, creating users, etc.
In YAML:

# app/config/routing.yml

fos_user_security:
    resource: "@FOSUserBundle/Resources/config/routing/security.xml"

fos_user_profile:
    resource: "@FOSUserBundle/Resources/config/routing/profile.xml"
    prefix: /profile

fos_user_register:
    resource: "@FOSUserBundle/Resources/config/routing/registration.xml"
    prefix: /register

fos_user_resetting:
    resource: "@FOSUserBundle/Resources/config/routing/resetting.xml"
    prefix: /resetting

fos_user_change_password:
    resource: "@FOSUserBundle/Resources/config/routing/change_password.xml"
    prefix: /profile

Step 7: Update your database schema

Now you have to update your database schema for your User entity which you have created.

The command to update schema is as follow:

 >php app/console doctrine:schema:update --force

Now you can run the fos bundle in your browser.

Below is User class which I have created for different fields by extending the FOS User Bundle.

 
namespace Jigar\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;

/**
 * User
 *
 * @ORM\Table(name="fos_user")
 * @ORM\Entity(repositoryClass="Jigar\UserBundle\Entity\UserRepository")
 */
class User extends BaseUser
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
    /**
     * @var string $firstname
     *
     * @ORM\Column(name="firstName", type="string", length=255)
     */
    protected $firstname;
    /**
     *
     * @var string
     *
     * @ORM\Column(name="lastname", type="string", length=255)
     */
    protected $lastname;
    /**
     *
     * @var string
     *
     * @ORM\Column(name="gender", type="string", length=1)
     */
    protected $gender;
   
    /**
     *
     * @var string
     *
     * @ORM\Column(name="hobby", type="array")
     */
    protected $hobby;
   
    /**
     *
     * @var string
     *
     * @ORM\Column(name="country", type="string", length=55)
     */
    protected $country;

    /**
     *
     * @var string
     *
     * @ORM\Column(name="phone", type="string", length=15)
     */
    protected $phone;

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

    /**
     * Set firstname
     *
     * @param string $firstname
     * @return User
     */
    public function setFirstname($firstname)
    {
        $this->firstname = $firstname;
   
        return $this;
    }

    /**
     * Get firstname
     *
     * @return string
     */
    public function getFirstname()
    {
        return $this->firstname;
    }

    /**
     * Set lastname
     *
     * @param string $lastname
     * @return User
     */
    public function setLastname($lastname)
    {
        $this->lastname = $lastname;
   
        return $this;
    }

    /**
     * Get lastname
     *
     * @return string
     */
    public function getLastname()
    {
        return $this->lastname;
    }

    /**
     * Set gender
     *
     * @param string $gender
     * @return User
     */
    public function setGender($gender)
    {
        $this->gender = $gender;
   
        return $this;
    }

    /**
     * Get gender
     *
     * @return string
     */
    public function getGender()
    {
        return $this->gender;
    }

    /**
     * Set hobby
     *
     * @param array $hobby
     * @return User
     */
    public function setHobby($hobby)
    {
        $this->hobby = $hobby;
   
        return $this;
    }

    /**
     * Get hobby
     *
     * @return array
     */
    public function getHobby()
    {
        return $this->hobby;
    }

    /**
     * Set phone
     *
     * @param string $phone
     * @return User
     */
    public function setPhone($phone)
    {
        $this->phone = $phone;
   
        return $this;
    }

    /**
     * Get phone
     *
     * @return string
     */
    public function getPhone()
    {
        return $this->phone;
    }

    /**
     * Set country
     *
     * @param string $country
     * @return User
     */
    public function setCountry($country)
    {
        $this->country = $country;
   
        return $this;
    }
    /**
     * Get country
     *
     * @return string
     */
    public function getCountry()
    {
        return $this->country;
    }
}

Overriding a Form Type:

FOSUserBundle provide functionality for registering new user, updating your profile, changing your password and much more. If you want to add more properties to the form then you must have to add properties into your User class as shown above. Then you can make your own registration form.

The first step is to create a new form type in your own bundle. The following class extends the base FOSUserBundle RegistrationFormType and then adds the custom fields.

namespace Jigar\UserBundle\Form\Type;

use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RegistrationFormType extends BaseType
{
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Jigar\UserBundle\Entity\User',
        ));
    }
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        $builder->add('firstname')
                ->add('lastname')
                ->add('gender', 'choice',array(
                    'choices' => array('m' => 'Male', 'f' => 'Female'),
                    'expanded' => true ))
                ->add('hobby', 'choice',
                        array('choices' => array(
                            'travelling'=> 'Travelling',
                            'photography'=> 'Photography',
                            'reading'   => 'Reading'
                            ),
                            'expanded' => 'true',
                            'multiple' => 'true',
                        ))
                ->add('country', 'choice', array(
                    'empty_value' => 'Choose an option',
                    'choices' => array(
                        'IN' => 'India',
                        'USA' => 'USA',
                        'AU' => 'Australia'
                    )
                ))
                ->add('phone');
    }
    public function getName()
    {
        return 'jigar_user_registration';
    }
}

Now you have created your custom form using FOS User Bundle so you have to declare it as a service.
Below is the example of configuring form type as a service in YAML format:

# src/Jigar/UserBundle/Resources/config/services.yml

services:

    jigar_user.registration.form.type:

        class: Jigar\UserBundle\Form\Type\RegistrationFormType

        arguments: [%fos_user.model.user.class%]

        tags:

            - { name: form.type, alias: jigar_user_registration }

Overriding of Controllers:

After that I have made a registration controller by overridding the registration controller of FOS User Bundle as following:

 namespace Jigar\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use FOS\UserBundle\Event\GetResponseUserEvent;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use FOS\UserBundle\Event\FilterUserResponseEvent;
class RegistrationController extends Controller
{
    public function registerAction(Request $request)
    {
        /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
        $formFactory = $this->container->get('fos_user.registration.form.factory');
        /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
        $userManager = $this->container->get('fos_user.user_manager');
        /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
        $dispatcher = $this->container->get('event_dispatcher');

        $user = $userManager->createUser();
        $user->setEnabled(true);

        $event = new GetResponseUserEvent($user, $request);
        $dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);

        if (null !== $event->getResponse()) {
            return $event->getResponse();
        }
       
        $form = $formFactory->createForm();
        $form->setData($user);

        if ('POST' === $request->getMethod()) {
            $form->bind($request);

            if ($form->isValid()) {
                $event = new FormEvent($form, $request);
                $dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);

                $userManager->updateUser($user);

                if (null === $response = $event->getResponse()) {
                    $url = $this->container->get('router')->generate('jigar_user_registration_confirmed');
                    $response = new RedirectResponse($url);
                }

                $dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

                return $response;
            }
        }

        return $this->container->get('templating')->renderResponse('JigarUserBundle:Registration:register.html.'.$this->getEngine(), array(
            'form' => $form->createView(),
        ));
    }
?>


Figure shows the registration page which I have created by using the FOS User Bundle.

FOSUserBundle Emails:

  •   Registration Confirmation

Requiring email confirmation for a new account is turned off by default. To enable it, update your configuration as follows:

# app/config/config.yml

fos_user:

    # ...

    registration:
        confirmation:
            enabled:    true

  Password Reset

The FOSUserBundle provides password reset functionality in a two-step process. First the user must request a password reset. After the request has been made, an email is sent containing a link to visit. Upon visiting the link, the user will be identified by the token contained in the url. When the user visits the link and the token is confirmed, the user will be presented with a form to enter in a new password.

  Default Mailer Implementations

The bundle comes with three mailer implementations. They are listed below by service id:

  • fos_user.mailer.default is the default implementation, and uses Swiftmailer to send emails.
  • fos_user.mailer.twig_swift uses Swiftmailer to send emails and Twig blocks to render the message.
  • fos_user.mailer.noop is a mailer implementation which performs no operation, so no emails are sent.

  Configuring the Sender Email Address

The FOSUserBundle default mailer allows you to configure the sender email address of the emails sent out by the bundle. You can configure the address globally or on a per email basis.

To configure the sender email address for all emails sent out by the bundle, simply update your fos_user config as follows:

# app/config/config.yml

fos_user:
    from_email:
        address:        noreply@symfonydemo.com
        sender_name:    Symfony Demo App

The bundle also provides the flexibility of allowing you to configure the sender email address for the emails individually.

To configure the sender email address for the user registration confirmation email update your fos_user config as follows:

# app/config/config.yml

fos_user:
    registration:
        confirmation:
            from_email:
                address:        registration@symfonydemo.com
                sender_name:    Symfony Demo Registration

You can similarly update the fos_user config to change the sender email address for the password reset request email:

# app/config/config.yml

fos_user:
    registration:
        confirmation:
            from_email:
                address:        registration@symfonydemo.com
                sender_name:    Symfony Demo Registration

  Sending HTML mails

The default mailer only supports sending plain text messages. If you want to send multipart messages, the easiest solution is to use the TwigSwiftMailer implementation instead. It expects your twig template to define 3 blocks:

  • subject containing the email subject
  • body_text rendering the plain text version of the message
  • body_html rendering the html mail


Here is how you can use it:

# app/config/config.yml

fos_user:

    # ...

    service:

        mailer: fos_user.mailer.twig_swift

    resetting:

        email:

            template: JigarUserBundle:User:resetting.email.html.twig

           {# src/Jigar/UserBundle/Resources/views/User/resetting.email.html.twig #}

{% block subject %}Resetting your password{% endblock %}

{% block body_text %}

{% autoescape false %}

Hello {{ user.username }} !

You can reset your email by accessing {{ confirmationUrl }}

Greetings,

the Symfony team

{% endautoescape %}

{% endblock %}

{% block body_html %}

{#

    You can of course render the html directly here.

    Including a template as done here allows keeping things DRY by using

    the template inheritance in it

#}

{% include 'JigarUserBundle:User:resetting_email.html.twig' %}

{% endblock %}