0

I'd like to insert a checkbox in my form with Symfony. I used CheckboxType in my UserType.php file for field "role". I would like to set the value "ROLE_ADMIN" for when the checkbox is checked. The value of a checkbox is a boolean and at the moment this is why I am stuck. I currently have this error message : Unable to transform value for property path "roles": Expected a Boolean. Which doesn't correspond with the value my database is asking (json type). I think I am missing a step and I don't know how I can move forward in the construction of my form.

How can I set my checkbox value so that "user" role value changes for "ROLE_ADMIN" (instead of "ROLE_USER") when checkbox is checked ?

Register.html.twig

{% extends 'base.html.twig' %}

{% block title %}Incris-toi !{% endblock %}

{% block main %}

<main>

    <header></header>

    {{form_start(userform)}}

        <div class="form container">
            <div class="form-floating mb-3">
                {{form_widget(userform.email, {'attr' : {'placeholder' : 'Mon adresse e-mail', 'class' : 'form-control'}})}}
                {{form_label(userform.email, 'Mon adresse e-mail', {'label_attr' : {'class' : 'label'}})}}
            </div>
            <div class="form-floating mb-3">
                {{form_widget(userform.password.first, {'attr' : {'placeholder' : 'Mon mot de passe', 'class' : 'form-control'}})}}
                {{form_label(userform.password.first, 'Mon mot de passe', {'label_attr' : {'class' : 'label'}})}}
            </div>
            <div class="form-floating">
                {{form_widget(userform.password.second, {'attr' : {'placeholder' : 'Confirmation de mon mot de passe', 'class' : 'form-control'}})}}
                {{form_label(userform.password.second, 'Confirmation de mon mot de passe', {'label_attr' : {'class' : 'label'}})}}
            </div>
            <div class="checkbox">
                <label class="remember-me-label">
                    {{form_widget(userform.roles)}}
                    <span class="checkmark"></span>
                    Je m'inscris uniquement en tant qu'organisateur.
                </label>
            </div>
            <button class="btn btn-lg btn-outline-primary mt-4 d-flex mx-auto">Je m'inscris</button>
        </div>

    {{form_end(userform)}}

</main>

{% endblock %}

UserType.php

<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('gender')
            ->add('lastname')
            ->add('firstname')
            ->add('birthdate')
            ->add('occupation')
            ->add('nationality')
            ->add('nativelanguage')
            ->add('wishedlanguages')
            ->add('email')
            ->add('password', PasswordType::class, [
                'mapped' => false
            ])
            ->add('password', RepeatedType::class, [
                'type' => PasswordType::class,
                'invalid_message' => 'Les deux mots de passe doivent être identiques.',
                'options' => ['attr' => ['class' => 'password-field']],
                'required' => true,
                'first_options'  => ['label' => 'Password'],
                'second_options' => ['label' => 'Repeat Password'],
            ])
            ->add('roles', CheckboxType::class, [
                'label' => 'Role :',
                'required' => false,
            ])
            ->add('Subcription', SubmitType::class);
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => User::class,
        ]);
    }
}

UserController.php

<?php

namespace App\Controller;

use App\Entity\User;
use App\Form\UserType;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

class UserController extends AbstractController
{
#[Route('/register', name: 'register', methods: ['GET', 'POST'])]
    public function createUser(
        Request $request,
        EntityManagerInterface $entityManagerInterface,
        UserPasswordHasherInterface $userPasswordHasherInterface
    ){
        $user = new User();
        $userform = $this->createForm(UserType::class, $user);
        $userform->handleRequest($request);

        if ($userform->isSubmitted() && $userform->isValid()) {
            $user->setRoles(["ROLE_USER"]);

            $plainPassword = $userform->get('password')->getData();
            $hashedPassword = $userPasswordHasherInterface->hashPassword($user, $plainPassword);
            $user->setPassword($hashedPassword);

            $entityManagerInterface->persist($user);
            $entityManagerInterface->flush();

            return $this->redirectToRoute('home');
        }

        return $this->render('front/register.html.twig', [
            'userform' => $userform->createView()
        ]);
    }
}
1
  • Take a look at the DataTransformer documentation. You'll have to return an array with the appropiate value for the roles field based on the boolean you get from the submission. Or you could do it in the controller with an unmapped field, but the first option is cleaner. Commented Sep 1, 2022 at 14:48

1 Answer 1

1

Here is an example of a dataTransformer for your use case.

The CallbackTransformer takes two callback functions as arguments. The first transforms the original value into a format that'll be used to render the field. The second does the reverse: it transforms the submitted value back into the format you'll use in your code.

This can be made into a separate file to reuse in other forms, this is described in the docs, but this is just an example.

// Remember to add this at the top of your UserType.
use Symfony\Component\Form\CallbackTransformer;

// Add this into your buildForm method.
$builder->get('roles')
    ->addModelTransformer(new CallbackTransformer(
        function ($arrayAsBool) {
            // Transform the array to a bool.
            // From the db to the form checkbox.
            return in_array("ROLE_ADMIN", $arrayAsBool);
        },
        function ($boolAsArray) {
            // Transform the bool back to an array.
            // From the form submit to the db.
            // For multiple roles this will need updating, for this example this will do.
            return $boolAsArray ? ["ROLE_ADMIN"] : ["ROLE_USER"];
        }
    ));

You wouldn't need this $user->setRoles(["ROLE_USER"]); in your UserController anymore to set default as it will set it in the form now.

Update/tweak to your needs.

Sign up to request clarification or add additional context in comments.

7 Comments

I am trying your solution so I did exactly what you said. It looks like it worked very well. Thank you. :)
@EmilieTossan, nice one, you can accept the answer if it worked for you :)
Hello Bossman. Apparently I have a syntax problem since I used dataTransformer. Can you give a help ? It would be great. stackoverflow.com/questions/73600720/…
Oh, I see I had to add ->add and a new builder inside it. ;)
Hello Bossman. When I validated the form but neither "ROLE_ADMIN" nor "ROLE_USER" was sent to database.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.