Symfony2:在 FormBuilder 中获取用户角色列表

Posted

技术标签:

【中文标题】Symfony2:在 FormBuilder 中获取用户角色列表【英文标题】:Symfony2: Getting the list of user roles in FormBuilder 【发布时间】:2012-06-30 02:22:35 【问题描述】:

我正在制作一个用于创建用户的表单,我想在创建用户时为他分配一个或多个角色。

如何获取security.yml 中定义的角色列表?

这是我目前的表单生成器:

public function buildForm(FormBuilder $builder, array $options)

    parent::buildForm($builder, $options);

    // add your custom fields
    $user = new User();
    $builder->add('regionUser');
    $builder->add('roles' ,'choice' ,array('choices' => $user->getRolesNames(),
            'required'  => true,
    ));


在 User.php

public function getRolesNames()
    return array(
        "ADMIN" => "Administrateur",
        "ANIMATOR" => "Animateur",
        "USER" => "Utilisateur",        
    );

当然,这个解决方案是行不通的,因为roles在数据库中被定义为位图,所以无法创建choices列表。

提前致谢。

【问题讨论】:

我认为@Mihai Aurelian 的回答更适合您的问题***.com/questions/11246774/… 【参考方案1】:

这不是您想要的,但它使您的示例有效:

use Vendor\myBundle\Entity\User;

public function buildForm(FormBuilder $builder, array $options)

    parent::buildForm($builder, $options);

    // add your custom fields
    $user = new User();
    $builder->add('regionUser');
    $builder->add('roles' ,'choice' ,array('choices' => User::getRolesNames(),
            'required'  => true,
    ));

但是关于从实体获取角色,也许你可以使用实体存储库的东西来查询数据库。

这是一个很好的示例,可以使用 queryBuilder 将所需内容放入实体存储库:

public function buildForm(FormBuilder $builder, array $options)

    parent::buildForm($builder, $options);

    // add your custom fields
    $user = new User();
    $builder->add('regionUser');
    $builder->add('roles' ,'entity' array(
                 'class'=>'Vendor\MyBundle\Entity\User',
                 'property'=>'roles',
                 'query_builder' => function (\Vendor\MyBundle\Entity\UserRepository $repository)
                 
                     return $repository->createQueryBuilder('s')
                            ->add('orderBy', 's.sort_order ASC');
                 
                )
          );

http://inchoo.net/tools-frameworks/symfony2-entity-field-type/

【讨论】:

感谢您的回答。我总是可以从数据库中获取角色(如果没有其他方法,也许我会这样做),但我实际上的目标是获取我在安全中定义的角色。在 User 中定义它们只是一种应对方法,但是如果您知道如何获取在 security 中定义的角色,我会接受! 愚蠢的我 :) 您也许应该使用 Yaml 解析器将 security.yml 转换为您将读取的数组。【参考方案2】:
//FormType
use Symfony\Component\Yaml\Parser;

function getRolesNames()
        $pathToSecurity = /var/mydirectory/app/config/security.yml
        $yaml = new Parser();
        $rolesArray = $yaml->parse(file_get_contents($pathToSecurity ));

        return $rolesArray['security']['role_hierarchy']['ROLE_USER'];

这是迄今为止我发现从配置文件中获取或设置我想要的内容的最佳方式。

勇气

【讨论】:

我不完全同意这个建议......但也想改进这个因为硬编码文件路径让我感到困惑。 $pathToSecurity = __DIR__ . '/../../../..' . '/app/config/security.yml';` 将与使用没有很好的oo味道的全局变量相同..将这些东西注入容器总是更好(容器参数security.role_hierarchy.roles)【参考方案3】:

您可以通过此方法获取可访问角色的列表:

Symfony\Component\Security\Core\Role\RoleHierarchy::getReachableRoles(array $roles)

它似乎返回了数组$roles 参数中角色可访问的所有角色。它是 Symfony 的内部服务,其 ID 为 security.role_hierarchy 并且不公开(您必须明确地将其作为参数传递,它无法从服务容器访问)。

【讨论】:

除了这个好的答案之外,我还为RolesType 创建了一个gist 以直接在UserFormType 中使用【参考方案4】:

security.role_hierarchy.roles 容器参数将角色层次结构保存为一个数组。您可以对其进行概括以获取已定义的角色列表。

【讨论】:

对于那些不明白它的意思的人,在你的控制器中使用它:$roles = $this->get('security.role_hierarchy'); 我的返回空,当我 var_dump 这甚至不回显 null,知道为什么吗? 不确定何时更改,但在 2.5 中是 'security.role_hierarchy' @sepehr 当之无愧的答案。【参考方案5】:

这是我所做的:

表单类型:

use FTW\GuildBundle\Entity\User;

class UserType extends AbstractType


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

    $builder
        ->add('username')
        ->add('email')
        ->add('enabled', null, array('required' => false))
        ->add('roles', 'choice', array(
        'choices' => User::getRoleNames(),
        'required' => false,'label'=>'Roles','multiple'=>true
    ))
        ->add('disableNotificationEmails', null, array('required' => false));

在实体中:

use Symfony\Component\Yaml\Parser; ...

static function getRoleNames()

    $pathToSecurity = __DIR__ . '/../../../..' . '/app/config/security.yml';
    $yaml = new Parser();
    $rolesArray = $yaml->parse(file_get_contents($pathToSecurity));
    $arrayKeys = array();
    foreach ($rolesArray['security']['role_hierarchy'] as $key => $value)
    
        //never allow assigning super admin
        if ($key != 'ROLE_SUPER_ADMIN')
            $arrayKeys[$key] = User::convertRoleToLabel($key);
        //skip values that are arrays --- roles with multiple sub-roles
        if (!is_array($value))
            if ($value != 'ROLE_SUPER_ADMIN')
                $arrayKeys[$value] = User::convertRoleToLabel($value);
    
    //sort for display purposes
    asort($arrayKeys);
    return $arrayKeys;


static private function convertRoleToLabel($role)

    $roleDisplay = str_replace('ROLE_', '', $role);
    $roleDisplay = str_replace('_', ' ', $roleDisplay);
    return ucwords(strtolower($roleDisplay));

请务必提供反馈...我使用了其他答案的一些建议,但我仍然觉得这不是最好的解决方案!

【讨论】:

【参考方案6】:

您可以为此创建服务并注入“security.role_hierarchy.roles”参数。

服务定义:

acme.user.roles:
   class: Acme\DemoBundle\Model\RolesHelper
   arguments: ['%security.role_hierarchy.roles%']

服务类:

class RolesHelper

    private $rolesHierarchy;

    private $roles;

    public function __construct($rolesHierarchy)
    
        $this->rolesHierarchy = $rolesHierarchy;
    

    public function getRoles()
    
        if($this->roles) 
            return $this->roles;
        

        $roles = array();
        array_walk_recursive($this->rolesHierarchy, function($val) use (&$roles) 
            $roles[] = $val;
        );

        return $this->roles = array_unique($roles);
    

您可以像这样在控制器中获取角色:

$roles = $this->get('acme.user.roles')->getRoles();

【讨论】:

我建议在构造函数中遍历层次结构以避免每次调用 getRoles 时都这样做。 你是对的。将答案编辑为仅调用 array_walk_recursive 一次。 每次调用getRoles() 时,您仍在这样做。服务启动时为什么不做呢? 好吧,当您第一次调用 getRoles 时,它会将数组保存在 $roles 属性中。每隔一次,它将从此属性返回角色。在我的应用程序中,RolesHelper 类还具有其他方法(它不仅用于此目的)。如果我把array_walk放在构造函数中,它会一直被调用,即使我不调用getRoles()。即使您仅将此类用于角色列表,将来您也可能会向其添加其他方法,因此保持构造函数干净似乎更合乎逻辑。【参考方案7】:

在 Symfony 2.7 中,在控制器中你必须使用 $this->getParameters() 来获取角色:

$roles = array();
foreach ($this->getParameter('security.role_hierarchy.roles') as $key => $value) 
    $roles[] = $key;

    foreach ($value as $value2) 
        $roles[] = $value2;
    

$roles = array_unique($roles);

【讨论】:

【参考方案8】:

为了正确表示您的角色,您需要递归。角色可以扩展其他角色。

我使用 security.yml 中的以下角色作为示例:

ROLE_SUPER_ADMIN: ROLE_ADMIN
ROLE_ADMIN:       ROLE_USER
ROLE_TEST:        ROLE_USER

您可以通过以下方式获得此角色:

$originalRoles = $this->getParameter('security.role_hierarchy.roles');

递归示例:

private function getRoles($originalRoles)

    $roles = array();

    /**
     * Get all unique roles
     */
    foreach ($originalRoles as $originalRole => $inheritedRoles) 
        foreach ($inheritedRoles as $inheritedRole) 
            $roles[$inheritedRole] = array();
        

        $roles[$originalRole] = array();
    

    /**
     * Get all inherited roles from the unique roles
     */
    foreach ($roles as $key => $role) 
        $roles[$key] = $this->getInheritedRoles($key, $originalRoles);
    

    return $roles;


private function getInheritedRoles($role, $originalRoles, $roles = array())

    /**
     * If the role is not in the originalRoles array,
     * the role inherit no other roles.
     */
    if (!array_key_exists($role, $originalRoles)) 
        return $roles;
    

    /**
     * Add all inherited roles to the roles array
     */
    foreach ($originalRoles[$role] as $inheritedRole) 
        $roles[$inheritedRole] = $inheritedRole;
    

    /**
     * Check for each inhered role for other inherited roles
     */
    foreach ($originalRoles[$role] as $inheritedRole) 
        return $this->getInheritedRoles($inheritedRole, $originalRoles, $roles);
    

输出:

array (
  'ROLE_USER' => array(),
  'ROLE_TEST' => array(
                        'ROLE_USER' => 'ROLE_USER',
  ),
  'ROLE_ADMIN' => array(
                        'ROLE_USER' => 'ROLE_USER',
  ),
  'ROLE_SUPER_ADMIN' => array(
                        'ROLE_ADMIN' => 'ROLE_ADMIN',
                        'ROLE_USER' => 'ROLE_USER',
  ),
)

【讨论】:

autowireautogofigure 设置为 true 时可用。我个人根本不喜欢这个..!如果没有,请像以前一样在services.yml 中设置RolesType【参考方案9】:

如果需要获取某个角色的所有继承角色:

use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchy;

private function getRoles($role)

    $hierarchy = $this->container->getParameter('security.role_hierarchy.roles');
    $roleHierarchy = new RoleHierarchy($hierarchy);
    $roles = $roleHierarchy->getReachableRoles([new Role($role)]);
    return array_map(function(Role $role)  return $role->getRole(); , $roles);

然后调用这个函数:$this->getRoles('ROLE_ADMIN');

【讨论】:

【参考方案10】:

在 Symfony 3.3 中,您可以按如下方式创建 RolesType.php:

<?php

namespace AppBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;

/**
 * @author Echarbeto
 */
class RolesType extends AbstractType 

  private $roles = [];

  public function __construct(RoleHierarchyInterface $rolehierarchy) 
    $roles = array();
    array_walk_recursive($rolehierarchy, function($val) use (&$roles) 
      $roles[$val] = $val;
    );
    ksort($roles);
    $this->roles = array_unique($roles);
  

  public function configureOptions(OptionsResolver $resolver) 
    $resolver->setDefaults(array(
        'choices' => $this->roles,
        'attr' => array(
            'class' => 'form-control',
            'aria-hidden' => 'true',
            'ref' => 'input',
            'multiple' => '',
            'tabindex' => '-1'
        ),
        'required' => true,
        'multiple' => true,
        'empty_data' => null,
        'label_attr' => array(
            'class' => 'control-label'
        )
    ));
  

  public function getParent() 
    return ChoiceType::class;
  


然后将其添加到表单中,如下所示:

$builder->add('roles', RolesType::class,array(
          'label' => 'Roles'
      ));

重要的是还必须包含每个角色,例如: ROLE_ADMIN:[ROLE_ADMIN,ROLE_USER]

【讨论】:

以上是关于Symfony2:在 FormBuilder 中获取用户角色列表的主要内容,如果未能解决你的问题,请参考以下文章

使用 formbuilder 在组合框中选择默认值

添加 CSS 到表单类型是 Symfony2

如何将类属性设置为 Symfony2 表单输入

Symfony2,如何使表单标签类/属性与​​其输入不同?

Symfony2 项目中的 Doctrine2 映射问题

如何在formbuilder中从JSON模式生成表单