I have a user edit form where I would like to administer the roles assigned to a user.
Currently I have a multi-select list, but I have no way of populating it with
You can make your own type and then pass service container, from which you can then retrieve role hierarchy.
First, create your own type:
class PermissionType extends AbstractType
{
private $roles;
public function __construct(ContainerInterface $container)
{
$this->roles = $container->getParameter('security.role_hierarchy.roles');
}
public function getDefaultOptions(array $options)
{
return array(
'choices' => $this->roles,
);
);
public function getParent(array $options)
{
return 'choice';
}
public function getName()
{
return 'permission_choice';
}
}
Then you need to register your type as service and set parameters:
services:
form.type.permission:
class: MyNamespace\MyBundle\Form\Type\PermissionType
arguments:
- "@service_container"
tags:
- { name: form.type, alias: permission_choice }
Then during creation of form, simply add *permission_choice* field:
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('roles', 'permission_choice');
}
If you want to get only single list of roles without hierarchy, then you need to flat hierarchy somehow. One of possible solution is this:
class PermissionType extends AbstractType
{
private $roles;
public function __construct(ContainerInterface $container)
{
$roles = $container->getParameter('security.role_hierarchy.roles');
$this->roles = $this->flatArray($roles);
}
private function flatArray(array $data)
{
$result = array();
foreach ($data as $key => $value) {
if (substr($key, 0, 4) === 'ROLE') {
$result[$key] = $key;
}
if (is_array($value)) {
$tmpresult = $this->flatArray($value);
if (count($tmpresult) > 0) {
$result = array_merge($result, $tmpresult);
}
} else {
$result[$value] = $value;
}
}
return array_unique($result);
}
...
}
For to symfony 2.3:
<?php
namespace Labone\Bundle\UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
class PermissionType extends AbstractType
{
private $roles;
public function __construct(Container $container)
{
$roles = $container->getParameter('security.role_hierarchy.roles');
$this->roles = $this->flatArray($roles);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'choices' => $this->roles
));
}
public function getParent()
{
return 'choice';
}
public function getName()
{
return 'permission_choice';
}
private function flatArray(array $data)
{
$result = array();
foreach ($data as $key => $value) {
if (substr($key, 0, 4) === 'ROLE') {
$result[$key] = $key;
}
if (is_array($value)) {
$tmpresult = $this->flatArray($value);
if (count($tmpresult) > 0) {
$result = array_merge($result, $tmpresult);
}
} else {
$result[$value] = $value;
}
}
return array_unique($result);
}
}
The solution with $options array provided by webda2l does not work with Symfony 2.3. It gives me an error:
The option "roles" do not exist.
I found that we can pass parameters to the form type constructor.
In your controller :
$roles_choices = array();
$roles = $this->container->getParameter('security.role_hierarchy.roles');
# set roles array, displaying inherited roles between parentheses
foreach ($roles as $role => $inherited_roles)
{
foreach ($inherited_roles as $id => $inherited_role)
{
if (! array_key_exists($inherited_role, $roles_choices))
{
$roles_choices[$inherited_role] = $inherited_role;
}
}
if (! array_key_exists($role, $roles_choices))
{
$roles_choices[$role] = $role.' ('.
implode(', ', $inherited_roles).')';
}
}
# todo: set $role as the current role of the user
$form = $this->createForm(
new UserType(array(
# pass $roles to the constructor
'roles' => $roles_choices,
'role' => $role
)), $user);
In UserType.php :
class UserType extends AbstractType
{
private $roles;
private $role;
public function __construct($options = array())
{
# store roles
$this->roles = $options['roles'];
$this->role = $options['role'];
}
public function buildForm(FormBuilderInterface $builder,
array $options)
{
// ...
# use roles
$builder->add('roles', 'choice', array(
'choices' => $this->roles,
'data' => $this->role,
));
// ...
}
}
I took the idea from Marcello Voc, thanks to him!
In your controller
$editForm = $this->createForm(new UserType(), $entity, array('roles' => $this->container->getParameter('security.role_hierarchy.roles')));
In UserType :
$builder->add('roles', 'choice', array(
'required' => true,
'multiple' => true,
'choices' => $this->refactorRoles($options['roles'])
))
[...]
public function getDefaultOptions()
{
return array(
'roles' => null
);
}
private function refactorRoles($originRoles)
{
$roles = array();
$rolesAdded = array();
// Add herited roles
foreach ($originRoles as $roleParent => $rolesHerit) {
$tmpRoles = array_values($rolesHerit);
$rolesAdded = array_merge($rolesAdded, $tmpRoles);
$roles[$roleParent] = array_combine($tmpRoles, $tmpRoles);
}
// Add missing superparent roles
$rolesParent = array_keys($originRoles);
foreach ($rolesParent as $roleParent) {
if (!in_array($roleParent, $rolesAdded)) {
$roles['-----'][$roleParent] = $roleParent;
}
}
return $roles;
}