I am getting an error when saving data using a formType into a model that has a many to many relationship.
The error is
Catchable Fatal Error: Argument 1 passed to AppBundle\Entity\User::removeRole() must be an instance of AppBundle\Entity\Role, string given, called in /Code/project/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 616 and defined
What is the best way to inject the Role model into the form?
The model has the following:
/**
* @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* @ORM\JoinTable(name="user_roles")
*/
private $roles;
/**
* Remove role
*
* @param Role $role
*/
public function removeRole(Role $role)
{
$this->roles->removeElement($role);
}
/**
* Add role
*
* @param Role $role
*
* @return User
*/
public function addRole(Role $role)
{
$this->roles[] = $role;
return $this;
}
There is the reverse in Role
/**
* @ORM\ManyToMany(targetEntity="User", mappedBy="roles")
*/
private $users;
To save the data in the controller I have
$user = new User();
$form = $this->createForm(UserType::class, $user);
$form->handleRequest($request);
if( $form->isSubmitted() && $form->isValid() ){
$encryptedPassword = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($encryptedPassword);
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('user_list'));
}
return $this->render('user/new.html.twig', array('form' => $form->createView()));
And the FormType has
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('email')
->add('username')
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'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('roles', EntityType::class, array(
'class' => 'AppBundle:Role',
'choice_label' => 'name',
'multiple' => true
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User'
));
}
}
I have worked out and it was a little gotcha that was the issue is and may cause others the same.
It is because:
class User implements UserInterface
which requires
/**
* @return mixed
*/
public function getRoles()
{
return ARRAY|STRING;
}
To get around this I create:
/**
* @ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* @ORM\JoinTable(name="user_roles")
*/
private $userRoles;
Everything then comes of getUserRoles and works fine.