How to customize form field based on user roles in Symfony2/3?
Asked Answered
I

2

13

Is there a correct way to customize a form depending on the role of the user that requests it?

My scenario is pretty simple: I need to hide some fields if the user has not the ROLE_ADMIN granted. I tried to avoid the field display on Twig, but

  {% if is_granted('ROLE_ADMIN') %}
              {{form_row(form.field)}}
  {% endif %}

not works, because the form builder bypass this check.

Symfony version: 2.8.2

EDIT

Thanks to the @Rooneyl suggestion I've found the solution:

At first, you need to add the 'role' key to the options parameter. So, in the configureOptions() $options['role'] is always ROLE_USER.

/**
 * @param OptionsResolver $resolver
 */
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'data_class' => 'MyBundle\Entity\Ticket',
        'role' => 'ROLE_USER'
    ));
}

Then in the controller you have to pass the getRoles() array:

$user_roles = $this->getUser()->getRoles();
$form = $this->createForm('MyBundle\Form\TicketType', $ticket, array('role' => $user_roles));
Interlanguage answered 16/2, 2016 at 12:59 Comment(1)
have updated the codeWalkon
W
7

You could use an option passed to the form builder to say what elements are generated.
This way you can change the content and validation that gets done (using validation_groups).
For example, your controller (assuming roles is an array);
you controller;

$form = $this->createForm(new MyType(), $user, ['role' => $this->getUser()->getRoles()]);

And your form:

<?php 
namespace AppBundle\Form\Entity;

use AppBundle\Entity\UserRepository;
use Symfony\Component\Form\AbstractType,
    Symfony\Component\Form\FormBuilderInterface,
    Symfony\Component\OptionsResolver\OptionsResolver;

class MyType extends AbstractType 
{
    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User',
            'validation_groups' => ['create'],
            'role' => ['ROLE_USER']
        ));
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // dump($options['roles']);
        if (in_array('ROLE_ADMIN', $options['role'])) {
            // do as you want if admin
            $builder
                ->add('name', 'text');
        } else {
            $builder
                ->add('supername', 'text');
        }
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'appbundle_my_form';
    }

}
Walkon answered 16/2, 2016 at 13:16 Comment(7)
When I try to use the options parameter Symfony returns this error: The option "role" does not exist.. Why?Interlanguage
I think the $options['role'] has to be getUser()->getRoles().Interlanguage
The code may not be complete, I'm mobile so can't test it at the moment. I will run it and give a complete solution in a bit, but the theory it is sound.Walkon
The main problem seems to be related to the $options array. It's not possible to add the role key.Interlanguage
Rooneyl's example uses roles both in the $options array and in the configureOptions() function - and it sounds like you are trying to use role instead...perhaps you have role in one place and roles in the other?Overwrought
I edited Rooneyl's code. It now has role instead of roles. It works for me. I already tested it.Interlanguage
@Walkon how to do that if you're using FOSUserBundle? Pls badly need help.Attitudinize
B
19

You can do it in your form.

Make a service for your form

app.form.type.task:
    class: AppBundle\Form\FormType
    arguments: ["@security.authorization_checker"]
    tags:
        - { name: form.type }

In your FormType, add a constructor to get your service.

private $authorization;
public function __construct(AuthorizationChecker $authorizationChecker)
{
    $this->authorization = $authorizationChecker;
}

Then, in your builder, you will be able to check user permission

$builder->add('foo');
if($this->authorization->isGranted('ROLE_ADMIN'))
{
   $builder->add('bar');
}

And then, finally, you can render your form

{% if form.formbar is defined %}
    {{ form_row(form.formbar ) }}
{% endif %}

Please note that it mean that your field may be null. Because maybe you want to see some of them visible by some users and others not.

Else, you can set a default value in your entity construct method, to make sure value won't be null if user don't/can't fill it.

Bedim answered 16/2, 2016 at 13:15 Comment(1)
Simply great. ThanksDecree
W
7

You could use an option passed to the form builder to say what elements are generated.
This way you can change the content and validation that gets done (using validation_groups).
For example, your controller (assuming roles is an array);
you controller;

$form = $this->createForm(new MyType(), $user, ['role' => $this->getUser()->getRoles()]);

And your form:

<?php 
namespace AppBundle\Form\Entity;

use AppBundle\Entity\UserRepository;
use Symfony\Component\Form\AbstractType,
    Symfony\Component\Form\FormBuilderInterface,
    Symfony\Component\OptionsResolver\OptionsResolver;

class MyType extends AbstractType 
{
    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User',
            'validation_groups' => ['create'],
            'role' => ['ROLE_USER']
        ));
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // dump($options['roles']);
        if (in_array('ROLE_ADMIN', $options['role'])) {
            // do as you want if admin
            $builder
                ->add('name', 'text');
        } else {
            $builder
                ->add('supername', 'text');
        }
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'appbundle_my_form';
    }

}
Walkon answered 16/2, 2016 at 13:16 Comment(7)
When I try to use the options parameter Symfony returns this error: The option "role" does not exist.. Why?Interlanguage
I think the $options['role'] has to be getUser()->getRoles().Interlanguage
The code may not be complete, I'm mobile so can't test it at the moment. I will run it and give a complete solution in a bit, but the theory it is sound.Walkon
The main problem seems to be related to the $options array. It's not possible to add the role key.Interlanguage
Rooneyl's example uses roles both in the $options array and in the configureOptions() function - and it sounds like you are trying to use role instead...perhaps you have role in one place and roles in the other?Overwrought
I edited Rooneyl's code. It now has role instead of roles. It works for me. I already tested it.Interlanguage
@Walkon how to do that if you're using FOSUserBundle? Pls badly need help.Attitudinize

© 2022 - 2024 — McMap. All rights reserved.