Search code examples
phpformbuildersymfony5

(solved)(symfony5 formbuilder) How to repopulate checkboxes with data from a json array?


The users role data is stored as a json array in Postgres.

e.g. ["ROLE_SUPER_ADMIN","ROLE_3","ROLE_4"]

Here's function to edit the user. I pass the retrieved user object to the form.

public function edit($id, Request $request, TranslatorInterface $translator) {

    $user = $this->getDoctrine()
        ->getRepository(User::class)
        ->find($id);

    if (!$user) {
        throw $this->createNotFoundException();
    }

    $this->denyAccessUnlessGranted("USER_EDIT", $user);

    $form = $this->createForm(UserType::class, $user);

    if (!$this->security->isGranted("ROLE_USER_ADMIN")) {

        $form->remove("roles");
    }

    $form->remove("password");

    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {

        if ($this->security->isGranted("ROLE_USER_ADMIN")) {

            $roles = $form->get("roles")->getData();

            $array= [];

            if ($roles) {

                foreach ($roles as $role) {

                    $array[] = $role->getName();
                }
            }

            $user->setRoles($array);
        }

        $user->setTsModified(new \DateTime());

        $manager = $this->getDoctrine()->getManager();
        $manager->flush();

        $this->addFlash(
            "success",
            $translator->trans("user_edit_success")
        );

        if ($this->security->isGranted("ROLE_USER_ADMIN")) {

            return $this->redirectToRoute("user_list");
        }

        return $this->redirectToRoute("dashboard");
    }

    return $this->render("user/edit.html.twig", [
        "user" => $user,
        "user_edit" => $form->createView()
    ]);
}

And here's the UserType (the form) to be filled. I removed all data except the role selection for this post.

class UserType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options) {

        $builder
            ->add("roles", EntityType::class, [
                "class" => Role::class,
                "placeholder" => "select",
                "choice_label" => "name",
                "choice_value" => "name",
                "label" => "roles",
                "expanded" => true,
                "multiple" => true,
                "required" => false,
            ])
            ->add("save", SubmitType::class, ["label" => "save"])
        ;
    }

    public function configureOptions(OptionsResolver $resolver) {

        $resolver->setDefaults([
            "data_class" => User::class,
        ]);
    }
}

It should populate checkboxes like this but it doesn't. Now I have to (re)select roles everytime I edit a user since no checked boxes means no roles selected (this is intended).

Am I building the form wrong or should there be some extra steps before calling $form = $this->createForm(UserType::class, $user); in the edit function?

Edit 8.6.2020

I just noticed formbuilder expects Role objects but now its getting strings. When selecting roles I only take the name (string) from the Role object (Role object contains id and name).

Have to see if I can transform the array of strings back to Role objects before building the form.

Solved 8.6.2020

Changed EntityType selection to ChoiceType which gets populated by the OptionsResolver.

User edit function. Remember to remove ->getName(); from the loop since the submitted value is no longer an object but a string.

$roles1 = $this->getDoctrine()
    ->getRepository(Role::class)
    ->findAll();

foreach ($roles1 as $role) {
    $roles[] = $role->getName();
}

$form = $this->createForm(UserType::class, $user, [
    "roles" => $roles
]);

                foreach ($roles as $role) {

                    $array[] = $role;
                }

UserType.

    ->add("roles", ChoiceType::class, [
        "choices" => $options["roles"],
        "choice_label" => function($key, $index) {
            return $key;
        },
        "expanded" => true,
        "multiple" => true,
        "required" => false,
        "label" => "roles",
    ])

And the OptionsResolver part of it.

public function configureOptions(OptionsResolver $resolver) {

    $resolver->setDefaults([
        "data_class" => User::class,
        "roles" => null
    ]);
}

Solution

  • Changed EntityType selection to ChoiceType which gets populated by the OptionsResolver.

    User edit function. Remember to remove ->getName(); from the loop since the submitted value is no longer an object but a string.

    $roles1 = $this->getDoctrine()
        ->getRepository(Role::class)
        ->findAll();
    
    foreach ($roles1 as $role) {
        $roles[] = $role->getName();
    }
    
    $form = $this->createForm(UserType::class, $user, [
        "roles" => $roles
    ]);
    
                    foreach ($roles as $role) {
    
                        $array[] = $role;
                    }
    

    UserType.

    ->add("roles", ChoiceType::class, [
        "choices" => $options["roles"],
        "choice_label" => function($key, $index) {
            return $key;
        },
        "expanded" => true,
        "multiple" => true,
        "required" => false,
        "label" => "roles",
    ])
    

    And the OptionsResolver part of it.

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