Search code examples
phparraysformssymfony

Symfony - ChoiceType Form - Array to string conversion error


I'm trying to make a form with a choice type input on the field roles on my entity user. However, Symfony reply me an error "Array to string conversion error"

My roles don't have to be an array because the are not multiple (role hierarchy in security.yaml).

Here is my Form Builder :

 $builder
        ->add('email')
        ->add('prenom')
        ->add('nom')
        ->add('password', PasswordType::Class)
        ->add('confirm_password', PasswordType::Class)
        ->add('roles', ChoiceType::class, [
            'choices'  => [
                'Utilisateur' => "ROLE_USER",
                'Valideur' => "ROLE_VALIDEUR",
                'Administrateur' => "ROLE_ADMIN",                    
            ],

        ])

And my User entity :

class User implements UserInterface
{
/**
 * @ORM\Id()
 * @ORM\GeneratedValue()
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @ORM\Column(type="string", length=255)
 * @Assert\Length(min="8", minMessage="Votre mot de passe doit contenir au moins 8 carractères")
 */
private $password;

/**
 * @Assert\EqualTo(propertyPath="password", message="Vous n'avez pas tapé le même mot de passe")
 */
private $confirm_password;

/**
 * @ORM\Column(type="simple_array")
 */
private $roles = array();

/**
 * @ORM\Column(type="string", length=255)
 * @Assert\NotBlank
 */
private $prenom;

/**
 * @ORM\Column(type="string", length=255)
 * @Assert\NotBlank
 */
private $nom;

/**
 * @ORM\Column(type="string", length=255)
 * @Assert\Email()
 * @Assert\NotBlank
 */
private $email;



public function getId(): ?int
{
    return $this->id;
}

public function getPassword(): ?string
{
    return $this->password;
}

public function setPassword(string $password): self
{
    $this->password = $password;

    return $this;
}

public function getConfirmPassword(): ?string
{
    return $this->confirm_password;
}

public function setConfirmPassword(string $confirm_password): self
{
    $this->confirm_password = $confirm_password;

    return $this;
}

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

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

    return $this;
}

public function getPrenom(): ?string
{
    return $this->prenom;
}

public function setPrenom(?string $prenom): self
{
    $this->prenom = $prenom;

    return $this;
}

public function getNom(): ?string
{
    return $this->nom;
}

public function setNom(?string $nom): self
{
    $this->nom = $nom;

    return $this;
}

public function getEmail(): ?string
{
    return $this->email;
}

public function setEmail(string $email): self
{
    $this->email = $email;

    return $this;
} 

public function getUsername(): ?string
{
    return $this->email;
}

public function eraseCredentials()
{
}

public function getSalt()
{
}
}

Could you explain me where are the problems in my user Entity (or somewhere else) please ?


Solution

  • The problem here is that Symfony trying to convert the value of the choice type to an array for user roles because the choice value is a string 'Utilisateur' => "ROLE_USER".

    The only solution is to not define a specific entity for the form and after apply values to the user entity.

    1. First, create the user type when the form will be :
    class UserType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            // The builder doesn't change
            $builder
                ->add('email')
                ->add('prenom')
                ->add('nom')
                ->add('password', PasswordType::Class)
                ->add('confirm_password', PasswordType::Class)
                ->add('roles', ChoiceType::class, [
                    'choices' => [
                        'Utilisateur' => 'ROLE_USER',
                        'Validateur' => 'ROLE_VALIDATEUR',
                        'Administrateur' => 'ROLE_ADMIN'
                    ]
                ])
            ;
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([]);
        }
    }
    
    1. After, create your controller and create the form :
    class UserController extends AbstractController
    {
        public function index(Request $request, EntityManagerInterface $manager)
        {
            // Create the form
            $form = $this->createForm(UserType::class);
    
            // Render the Twig file and pass the form as a view
            return $this->render('user/index.html.twig', ['form' => $form->createView()]);
        }
    }
    
    1. Then, you have to process the form and assign all properties to your entity like this :
    public function index(Request $request, EntityManagerInterface $manager)
    {
        $form = $this->createForm(UserType::class);
    
        // Processing the form from the request
        $form->handleRequest($request);
    
        // Check if form is submitted
        if($form->isSubmitted()) {
    
            // Fetch data from the form
            $data = $form->getData();
    
            // Create user from data
            $user = (new User)
                ->setEmail($data['email'])
                ->setPrenom($data['prenom'])
                ->setNom($data['nom'])
                ->setPassword($data['password'])
                ->setConfirmPassword($data['confirm_password'])
                // Here, we convert the string to an array
                ->setRoles([$data['roles']]);
    
            // Add the user to the database
            $manager->persist($user);
            $manager->flush();
        }
    
        return $this->render('user/index.html.twig', ['form' => $form->createView()]);
    }
    
    1. There is another problem with the code above: violations are not triggering and an error is throwing when there are conflicts between two entities (during $manager->flush()). The following code is the correction (we can use the validator to check if an entity is valid):
    // Request validator as injection dependency
    public function index(Request $request, EntityManagerInterface $manager, ValidatorInterface $validator)
    {
        $form = $this->createForm(UserType::class);
    
        // Set default values for validation variables
        $violations = null;
        $conflict = false;
    
        $form->handleRequest($request);
        if($form->isSubmitted()) {
            $data = $form->getData();
    
            $user = (new User)
                ->setEmail($data['email'])
                ->setPrenom($data['prenom'])
                ->setNom($data['nom'])
                ->setPassword($data['password'])
                ->setConfirmPassword($data['confirm_password'])
                // Here, we convert the string to an array
                ->setRoles([$data['roles']]);
    
            // Get violations from the user entity with the 'validate' function
            $violations = $validator->validate($user);
    
            // Check if there are no violations. If it is, the user is valid
            if($violations->count() === 0) {
                $manager->persist($user);
    
                // Catch a unique constraint violation exception during flushing
                try {
                    $manager->flush();
                } catch(UniqueConstraintViolationException $e) {
                    $conflict = true;
                }
            }
        }
    
        return $this->render('user/index.html.twig', [
            'form' => $form->createView(),
    
            // Pass violations and conflict
            'violations' => $violations,
            'conflict' => $conflict
        ]);
    }
    

    I hope that I help you!

    If you want create a field where we can select multiple roles, this link may help you (the website is French).