1
votes

I am creating a user form in which Roles are selected via form dropdown.My form and entity looks like this

//entity

private $roles;
public function getRoles()
{

    return $this->roles;
}

//form

class RoleType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'choices' => array(
            'ROLE_USER' => 'ROLE_USER',
            'ROLE_ADMIN' => 'ROLE_ADMIN',
        )
    ));
}

public function getParent()
{
    return 'choice';
}

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


class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username')
        ->add('password', 'repeated', array(
            'type' => 'password',
            'invalid_message' => 'The password fields must match.',
            'options' => array('attr' => array('class' => 'password-field')),
            'required' => true,
            'first_options'  => array('label' => 'Password'),
            'second_options' => array('label' => 'Repeat Password'),
        ))
        //->add('terms', 'checkbox', array(
           // 'property_path' => 'termsAccepted',
       // ))
        ->add('firstname')
        ->add('lastname')
        ->remove('photo')
        ->add('email', 'email')
        ->remove('status')
        ->remove('createdBy')
        ->remove('isActive')
        ->remove('dateCreated')
        ->remove('updatedBy')
        ->remove('dateUpdated')
        //->remove('roles', 'choice', array(
           // 'choices' => array('ROLE_USER' => 'ROLE_USER', 'ROLE_ADMIN' => 'ROLE_ADMIN'),
        //))
        ->add('roles', new RoleType(), array(
            'placeholder' => 'Choose Roles',
        ))
        //->add('terms', 'checkbox', array(
          //  'property_path' => 'termsAccepted',
           // 'attr' => array('class' => 'checkbox'),
       // ))
        ->add('Register', 'submit')
    ;
}

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

I have no problem when creating a user where Roles are selected via form dropdown.The problem is, during login, it throws errors that something must be in array, not in string

Catchable Fatal Error: Argument 4 passed to Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken::__construct() must be of the type array, string given, called in /var/www/

I change my user entity

 private $roles = array();
 public function setRoles($roles)
{
    $this->roles = $roles;

    return $this;
}

 public function getRoles()
 {
   return array($this->roles);
 }

The user can now successfully login, but it throws an error when adding another user in the form

The value of type "array" cannot be converted to a valid array key.

This is just a simple user bundle.I dont want to use another third party bundle like FOSUsersrBundle.Any idea how to fix this?

update

In the controller, I use to store this into database

public function createAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();

    //$form = $this->createForm(new RegistrationType(), new Registration());
    $form = $this->createForm(new RegistrationType(), new Users());

    $form->handleRequest($request);

    if ($form->isValid()) {
        $registration = new Users();
        $registration = $form->getData();
        $registration->setDateCreated(new \DateTime());
        //$registration->setRoles('ROLE_ADMIN');
        $registration->setPhoto('http://www.gravatar.com/avatar/'.md5(trim($request->get('email'))));
        $pwd=$registration->getPassword();
        $encoder=$this->container->get('security.password_encoder');
        $pwd=$encoder->encodePassword($registration, $pwd);
        $registration->setPassword($pwd);
        $em->persist($registration);
        $em->flush();

        $this->addFlash('danger', 'New User successfully added!');
        return $this->redirect($this->generateUrl('voters_list'));
    } else {
        $this->addFlash('danger', 'Some errors buddy cannot proceed, please check!');
        return $this->render('DuterteBundle:Security:register.html.twig',array(
            'form' => $form->createView())
        );
    }

update

My basic mapping

Project\Bundle\DuterteBundle\Entity\Users:
type: entity
table: users
repositoryClass: Project\Bundle\DuterteBundle\Repository\UsersRepository
#indexes:
    #role_id_idx:
        #columns:
            #- role_id
id:
    id:
        type: integer
        nullable: false
        unsigned: false
        comment: ''
        id: true
        generator:
            strategy: IDENTITY
fields:
    username:
        type: string
        nullable: false
        length: 50
        fixed: false
        comment: ''
    password:
        type: string
        nullable: false
        length: 32
        fixed: false
        comment: ''
    firstname:
        type: string
        nullable: true
        length: 50
        fixed: false
        comment: ''
    lastname:
        type: string
        nullable: true
        length: 50
        fixed: false
        comment: ''
    photo:
        type: string
        nullable: true
        length: 200
        fixed: false
        comment: ''
    email:
        type: string
        nullable: true
        length: 200
        fixed: false
        comment: ''
    status:
        type: string
        nullable: true
        length: 8
        fixed: false
        comment: ''
    roles:
        type: string
        nullable: true
        length: 100
        fixed: false
        comment: ''

in mysql, the users table includes the roles column where the roles are stored e.g 'ROLE_USERS',"ROLE_ADMIN' etc.Changing the getRoles function from the users entity

  public function getRoles()
  {
        return array('ROLE_USER'_)
  }

Will successfully logs a user, but the roles is always set to 'ROLE_USERS' even if int the database, a user's role is equivalent to 'ROLE_ADMIN'

1
How do you store the roles in the database? Can you post the mapping info from the user entity?Z-WolF
Well i see a lot of things happening that need some cleaning, while i work in the answer, can you please post the mapping info for your user entity?Z-WolF
@Z-WolF I already added my users.orm.ymlDanielle Rose Mabunga

1 Answers

5
votes

The biggest error in your code is returning an array inside an array in the getRoles() function:

 private $roles = array();
 public function setRoles($roles)
 {
    $this->roles = $roles;
    return $this;
 }

 public function getRoles()
 {
   return array($this->roles);
 }

Now in your mapping info you have the role as a string, this means your user only uses one role, this simplifies things a lot:

We're gonna change the name of the field because the security component needs an array and uses the function "getRoles()" because is inherited from the UserInterface or the AdvancedUserInterfaceClass but the form check the object in case there is already data on it (you can also edit and/or preload data to show in the form), it is necesary to do the equivalent changes in the database and the mappings info.

//entity
private $role;

public function setRole($role){
    $this->roles = $role;
    return $this;
}

public function getRole(){
    return $this->roles;
}

public function getRoles(){
    return array($this->role);
}

You dont need to set it as an array considering you persist it as a string, the security component does not register users, you do. And for that we're going to change your formtypes and your controller a bit.

//formtype
//I don't know what version you are using, but you dont need a separate formtype to add a choice and neither need to `remove` fields, you just don't `add` them.
//Remember to change the field in from 'roles' to 'role'
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('username')
        ->add('password', 'repeated', array(
            'type' => 'password',
            'invalid_message' => 'The password fields must match.',
            'options' => array('attr' => array('class' => 'password-field')),
            'required' => true,
            'first_options'  => array('label' => 'Password'),
            'second_options' => array('label' => 'Repeat Password'),
        ))
        ->add('firstname')
        ->add('lastname')
        ->add('email', 'email')
        ->add('role', 'choice', array(
            'choices' => array('ROLE_USER' => 'User', 'ROLE_ADMIN' => 'Admin'),
        ))
        ->add('Register', 'submit')
    ;
}

public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults(array(
        'data_class' => 'Project\Bundle\DuterteBundle\Entity\Users' //this is important
    ));
}
}

Now your Controller:

//Controller
//Also if you link a form to a entity you dont need the `getData()`

use Project\Bundle\DuterteBundle\Entity\Users;

public function createAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();
    $user = new Users();
    $form = $this->createForm(new RegistrationType(), $user);

    $form->handleRequest($request); //here, every form field that has an equivalent in the entity, fills the entity property instead, that's why you only need to set the role as a string ;)

    if ($form->isValid()) {
        $user->setDateCreated(new \DateTime());
        $user->setPhoto('http://www.gravatar.com/avatar/'.md5(trim($request->get('email'))));
        $pwd=$user->getPassword();
        $encoder=$this->container->get('security.password_encoder');
        $pwd=$encoder->encodePassword($user, $user->getPassword());
        $user->setPassword($pwd);
        $em->persist($user);
        $em->flush();

        $this->addFlash('danger', 'New User successfully added!');
        return $this->redirect($this->generateUrl('voters_list'));
    } else {
        $this->addFlash('danger', 'Some errors buddy cannot proceed, please check!');
        return $this->render('DuterteBundle:Security:register.html.twig',array(
            'form' => $form->createView())
        );
    }
}

as you can see is a lot more simple, easier and more clean. The getRoles is used by the security component to load roles into the app, is an array because the users can get more than one role, but for that you need a different schema.