26

Version : Symfony 2.2

I'm trying to add a default role when a user register on my website. I use FOSUserBundle and i see that when a user register the role field is empty in a database. I begin with this huge bundle and it's not very easy to understand. So i read all the documentation and i'm not sur what to do.

For now, i create an Event to add this role dynamically, but it doesn't work (i have no error but my database is still empty) I'm not even sur this is the good way to do that ?

My Event :

use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class AddDefaultRoleListener implements EventSubscriberInterface {

  private $container;

  public function __construct(Container $container)
  {
    $this->container = $container;
  }

  /**
   * {@inheritDoc}
   */
  public static function getSubscribedEvents()
  {
    return array(
        FOSUserEvents::REGISTRATION_SUCCESS => 'onAddDefaultRoleSuccess',
    );
  }

  public function onAddDefaultRoleSuccess(FormEvent $event)
  {
    $doctrine = $this->container->get('doctrine');
    $em = $doctrine->getManager();

    $user = $event->getForm()->getData();
    $user->addRole('ROLE_USER');
    //$user->setRoles(array('ROLE_USER'));

    $em->persist($user);
  }
}

As you see i create a simple event which listen on REGISTRATION_SUCCESS, but nothing seems to work. It's my first try with Events and services. So if someone has an advice, i'll take it :)

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Epok
  • 661
  • 1
  • 8
  • 16

6 Answers6

39

The recommended way to do it as indicated by a main contributor to the FOSUserBundle (in the comment here linked) is to register an Event Listener on the REGISTRATION_SUCCESS event and use the $event->getForm()->getData() to access the user and modify it. Following those guidelines, I created the following listener (which works!):

<?php

// src/Acme/DemoBundle/EventListener/RegistrationListener.php

namespace Acme\DemoBundle\EventListener;

use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listener responsible for adding the default user role at registration
 */
class RegistrationListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
        );
    }

    public function onRegistrationSuccess(FormEvent $event)
    {
        $rolesArr = array('ROLE_USER');

        /** @var $user \FOS\UserBundle\Model\UserInterface */
        $user = $event->getForm()->getData();
        $user->setRoles($rolesArr);
    }
}

Also, the service needs to be registered as follows:

// src/Acme/DemoBundle/Resources/config/services.yml
services:
    demo_user.registration_listener:
        class: Acme\DemoBundle\EventListener\RegistrationListener
        arguments: []
        tags:
            - { name: kernel.event_subscriber }

Notice that adding a default role in the User class __construct() may have some issues as indicated in this other answer.

Community
  • 1
  • 1
RayOnAir
  • 2,038
  • 2
  • 22
  • 33
  • Thanks for your answer and the links. You used the same logic than me with the REGISTRATION_SUCCESS event but it wasn't working for me when i asked the question last month (no persistence in db). But with the REGISTRATION_COMPLETED event it was ok. I'll try your code, thanks again. – Epok Jun 23 '13 at 09:06
  • This is a piece of the missing documentation for FOS User Bundle V2. – Peter Wooster Jul 08 '13 at 18:01
  • 1
    "// src/Acme/DemoBundle/Resources/config/services.xml" Isn't your example a YML file? Not that I'm complaining, I prefer YML. Might confuse some people however. – gan Aug 12 '13 at 06:14
  • This is the preferred way to do this. – kratos Sep 25 '13 at 18:42
  • Yes this is what I think about. Thanks :) In fact, the name of the event is not very descriptive. That's why when I want to do stuff just before the user is created, I don't know where to hook. – Anh Nguyen Mar 10 '14 at 14:08
  • 2
    Perhaps you could include a notice as to the version this will work on. FOSUserEvents is not included in the 1.3.x releases. It's a v2 addition which is still in development "dev-master". – Twifty Jul 19 '14 at 16:29
  • @RayOnAir: I followed your answer but unfortunately was not able to persist role into Database. I am using fosuerbundle V2, symfony 2.5.4 Can you please suggest what might be missing. – AkiShankar Nov 04 '14 at 07:24
  • @AnandS.Bahuguna I have used those same versions and I have no issues with persistance to the database. There must be something else going on with your code. – RayOnAir Nov 06 '14 at 15:24
  • Why is this the recommended way though? – Taylan Mar 10 '16 at 13:23
38

What i have done is override the entity constructor:

Here a piece of my Entity/User.php

public function __construct()
{
    parent::__construct();
    // your own logic
    $this->roles = array('ROLE_USER');
}

This is the lazy way. If you want the right and better way see the @RayOnAir answer

Community
  • 1
  • 1
alvaropgl
  • 830
  • 8
  • 19
  • 1
    This is the "easy way" . The @rayonair answer is the "correct way " – Rodrigo Lopez Guerra Jan 04 '17 at 13:58
  • @RodrigoLopezGuerra yeah! but keep it simple ;) – alvaropgl Jan 05 '17 at 14:43
  • 1
    sure ! :) . But is important to mark the difference. In the "right way" the role is being added once the regitration is done. In the "easy one" , is everytime you do new User(), and at least you always keep that in mind could lead to misbehaviours in the app or worst security holes – Rodrigo Lopez Guerra Jan 10 '17 at 14:05
4

I think @RayOnAir solution is right way of doing this. But it will not work due to FOS default role handling

to make possible to persist default role in database one need to override User::setRoles() method (add it to your User entity):

/**
 * Overriding Fos User class due to impossible to set default role ROLE_USER 
 * @see User at line 138
 * @link https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Model/User.php#L138
 * {@inheritdoc}
 */
public function addRole($role)
{
    $role = strtoupper($role);

    if (!in_array($role, $this->roles, true)) {
        $this->roles[] = $role;
    }

    return $this;
}

Tested under:

Symfony version 2.3.6, FOSUserBundle 2.0.x-dev

andrew
  • 409
  • 4
  • 7
  • The combination of @RayOnAir and your solution made it work for me on Symfony2.4.0 and https://github.com/FriendsOfSymfony/FOSUserBundle/commit/f7380a4e476302aa8ca16c9fd017d6d9adc9482e – No_name Dec 08 '13 at 04:42
  • Well It should work, `ROLE_USER` is the default role for FOSUserBundle https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Model/User.php#L283 – Cesar Sep 04 '14 at 04:59
  • Worked for me with no need to add this. Symfony 2.8.0, FOSUserBundle 2.0.x-dev – godzillante Dec 15 '15 at 10:02
  • @godzillante: It depends which value you use for the default user role. Using `"ROLE_USER"` can not work because the `User addRole()` method returns `if ($role === static::ROLE_DEFAULT)` (where `ROLE_DEFAULT` is `"ROLE_USER"`). – baris1892 Jan 12 '17 at 16:52
2

You can add an Event Subscriber to a Form Class and use the form event "formEvents::POST_SUBMIT"

<?php

//src/yourNS/UserBundle/Form/Type/RegistrationFormType.php

use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use yourNS\UserBundle\Form\EventListener\AddRoleFieldSubscriber;

class RegistrationFormType extends BaseType
{        
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        parent::buildForm($builder, $options);

        // add your custom field
        $builder->add('firstName')
            ->add('lastName')
            ->add('address')
            //...
            //...
            ->add('phone');
        $builder->addEventSubscriber(new AddRoleFieldSubscriber());
    }

    public function getName()
    {
        return 'yourNS_user_registration';
    }
}

Now the logic for adding the role field resides in it own subscriber class

<?php
//src/yourNS/UserBundle/Form/EventListener/AddRoleFieldSubscriber.php

namespace yourNS\UserBundle\Form\EventListener;

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class AddRoleFieldSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(FormEvents::POST_SUBMIT => 'setRole');
    }

    public function setRole(FormEvent $event)
    {
        $aRoles = array('ROLE_USER');

        /** @var $user \FOS\UserBundle\Model\UserInterface */
        $user = $event->getForm()->getData();
        $user->setRoles($aRoles);
    }
}
Oleksandr Savchenko
  • 642
  • 3
  • 10
  • 35
Mohamed Ben HEnda
  • 2,686
  • 1
  • 30
  • 44
  • Works using `$this->roles = ['ROLE_USER']` inside `construct()` method of the `Entity/User.php` class, and by previously calling `parent::__construct()` method. – Dan Costinel Feb 21 '17 at 11:01
1

Ok now it's working with that :

 public function onAddDefaultRoleSuccess(FilterUserResponseEvent $event)
{
    $doctrine = $this->container->get('doctrine');
    $em = $doctrine->getManager();

    $user = $event->getUser();
    $user->addRole('ROLE_BLOGGER');

    $em->persist($user);
    $em->flush();
}

I change my listener and know use REGISTRATION_COMPLETED. If someone has a better idea to do that, don't hesitate :)

Epok
  • 661
  • 1
  • 8
  • 16
  • 1
    If the user has no or limited access to website, I recommend using what @alwar wrote, event listener for this task is an overkill – Inoryy Apr 14 '13 at 13:28
  • I agree that this can better be done in the constructor, but the event listener example is a great resource for those who had been using the form handler previously. – Peter Wooster Jul 08 '13 at 17:47
0
public function __construct()
{
    parent::__construct();
    $this->setRoles(["ROLE_WHATEVER"]);
}
jelle woord
  • 193
  • 2
  • 16