如何在Symfony2中改变角色层次存储?

时间:2023-01-09 06:48:49

In my project I need to store role hierarchy in database and create new roles dynamically. In Symfony2 role hierarchy is stored in security.yml by default. What have I found:

在我的项目中,我需要在数据库中存储角色层次结构并动态创建新的角色。在Symfony2中,角色层次结构存储在安全性中。yml默认情况下。我发现:

There is a service security.role_hierarchy (Symfony\Component\Security\Core\Role\RoleHierarchy); This service receives a roles array in constructor:

有一个服务安全性。role_hierarchy核心组件(Symfony \ \安全\ \ \ RoleHierarchy作用);此服务在构造函数中接收一个角色数组:

public function __construct(array $hierarchy)
{
    $this->hierarchy = $hierarchy;

    $this->buildRoleMap();
}

and the $hierarchy property is private.

$hierarchy属性是私有的。

This argument comes in constructor from \Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension::createRoleHierarchy() which uses roles from config, as I understood:

这个参数来自\Symfony\Bundle\SecurityBundle\ SecurityBundle\ SecurityExtension::createRoleHierarchy(),它使用配置中的角色,正如我所理解的:

$container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']);

It seems me that the best way is to compile an array of roles from database and set it as an argument for the service. But I haven't yet understood how to do it.

我认为最好的方法是从数据库中编译角色数组并将其设置为服务的参数。但我还不知道怎么做。

The second way I see is to define my own RoleHierarchy class inherited from the base one. But since in the base RoleHierarchy class the $hierarchy property is defined as private, than I would have to redefine all the functions from the base RoleHierarchy class. But I don't think it is a good OOP and Symfony way...

我看到的第二种方式是定义自己继承自基类的RoleHierarchy类。但是由于在基RoleHierarchy类中,$hierarchy属性被定义为private,因此我必须从基RoleHierarchy类中重新定义所有函数。但我不认为这是一个好的OOP和Symfony方式……

6 个解决方案

#1


36  

The solution was simple. First I created a Role entity.

解决方案很简单。首先,我创建了一个角色实体。

class Role
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $name
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @ORM\ManyToOne(targetEntity="Role")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
     **/
    private $parent;

    ...
}

after that created a RoleHierarchy service, extended from the Symfony native one. I inherited the constructor, added an EntityManager there and provided an original constructor with a new roles array instead of the old one:

在创建了一个RoleHierarchy服务之后,从Symfony本地服务扩展。我继承了构造函数,在那里添加了一个EntityManager,并提供了一个具有新的角色数组的原始构造函数,而不是旧的角色数组:

class RoleHierarchy extends Symfony\Component\Security\Core\Role\RoleHierarchy
{
    private $em;

    /**
     * @param array $hierarchy
     */
    public function __construct(array $hierarchy, EntityManager $em)
    {
        $this->em = $em;
        parent::__construct($this->buildRolesTree());
    }

    /**
     * Here we build an array with roles. It looks like a two-levelled tree - just 
     * like original Symfony roles are stored in security.yml
     * @return array
     */
    private function buildRolesTree()
    {
        $hierarchy = array();
        $roles = $this->em->createQuery('select r from UserBundle:Role r')->execute();
        foreach ($roles as $role) {
            /** @var $role Role */
            if ($role->getParent()) {
                if (!isset($hierarchy[$role->getParent()->getName()])) {
                    $hierarchy[$role->getParent()->getName()] = array();
                }
                $hierarchy[$role->getParent()->getName()][] = $role->getName();
            } else {
                if (!isset($hierarchy[$role->getName()])) {
                    $hierarchy[$role->getName()] = array();
                }
            }
        }
        return $hierarchy;
    }
}

... and redefined it as a service:

…并将其重新定义为服务:

<services>
    <service id="security.role_hierarchy" class="Acme\UserBundle\Security\Role\RoleHierarchy" public="false">
        <argument>%security.role_hierarchy.roles%</argument>
        <argument type="service" id="doctrine.orm.default_entity_manager"/>
    </service>
</services>

That's all. Maybe, there is something unnecessary in my code. Maybe it is possible to write better. But I think, that main idea is evident now.

这是所有。也许,我的代码中有些不必要的东西。也许有可能写得更好。但我认为,现在的主要观点是显而易见的。

#2


13  

I had do the same thing like zIs (to store the RoleHierarchy in the database) but i cannot load the complete role hierarchy inside the Constructor like zIs did, because i had to load a custom doctrine filter inside the kernel.request event. The Constructor will be called before the kernel.request so it was no option for me.

我做了与zIs类似的事情(在数据库中存储RoleHierarchy),但是我不能像zIs那样在构造函数中加载完整的角色层次结构,因为我必须在内核中加载一个定制的doctrine过滤器。请求事件。构造函数将在内核之前被调用。请求,所以我没有选择。

Therefore I checked the security component and found out that Symfony calls a custom Voter to check the roleHierarchy according to the users role:

因此我检查了安全组件,发现Symfony调用了一个自定义投票者根据用户角色检查roleHierarchy:

namespace Symfony\Component\Security\Core\Authorization\Voter;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;

/**
 * RoleHierarchyVoter uses a RoleHierarchy to determine the roles granted to
 * the user before voting.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class RoleHierarchyVoter extends RoleVoter
{
    private $roleHierarchy;

    public function __construct(RoleHierarchyInterface $roleHierarchy, $prefix = 'ROLE_')
    {
        $this->roleHierarchy = $roleHierarchy;

        parent::__construct($prefix);
    }

    /**
     * {@inheritdoc}
     */
    protected function extractRoles(TokenInterface $token)
    {
        return $this->roleHierarchy->getReachableRoles($token->getRoles());
    }
}

The getReachableRoles Method returns all roles the user can be. For example:

geinsurancableroles方法返回用户可以扮演的所有角色。例如:

           ROLE_ADMIN
         /             \
     ROLE_SUPERVISIOR  ROLE_BLA
        |               |
     ROLE_BRANCH       ROLE_BLA2
       |
     ROLE_EMP

or in Yaml:
ROLE_ADMIN:       [ ROLE_SUPERVISIOR, ROLE_BLA ]
ROLE_SUPERVISIOR: [ ROLE_BRANCH ]
ROLE_BLA:         [ ROLE_BLA2 ]

If the user has the ROLE_SUPERVISOR role assigned the Method returns the roles ROLE_SUPERVISOR, ROLE_BRANCH and ROLE_EMP (Role-Objects or Classes, which implementing RoleInterface)

如果用户拥有ROLE_SUPERVISOR角色,则该方法返回ROLE_SUPERVISOR、ROLE_BRANCH和ROLE_EMP(角色对象或类,实现RoleInterface)的角色

Furthermore this custom voter will be disabled if there is no RoleHierarchy defined in the security.yaml

此外,如果security.yaml中没有定义RoleHierarchy,则将禁用此自定义投票者

private function createRoleHierarchy($config, ContainerBuilder $container)
    {
        if (!isset($config['role_hierarchy'])) {
            $container->removeDefinition('security.access.role_hierarchy_voter');

            return;
        }

        $container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']);
        $container->removeDefinition('security.access.simple_role_voter');
    }

To solve my issue I created my own custom Voter and extended the RoleVoter-Class, too:

为了解决我的问题,我创建了自己的自定义投票者,并扩展了rolevoter类:

use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Acme\Foundation\UserBundle\Entity\Group;
use Doctrine\ORM\EntityManager;

class RoleHierarchyVoter extends RoleVoter {

    private $em;

    public function __construct(EntityManager $em, $prefix = 'ROLE_') {

        $this->em = $em;

        parent::__construct($prefix);
    }

    /**
     * {@inheritdoc}
     */
    protected function extractRoles(TokenInterface $token) {

        $group = $token->getUser()->getGroup();

        return $this->getReachableRoles($group);
    }

    public function getReachableRoles(Group $group, &$groups = array()) {

        $groups[] = $group;

        $children = $this->em->getRepository('AcmeFoundationUserBundle:Group')->createQueryBuilder('g')
                        ->where('g.parent = :group')
                        ->setParameter('group', $group->getId())
                        ->getQuery()
                        ->getResult();

        foreach($children as $child) {
            $this->getReachableRoles($child, $groups);
        }

        return $groups;
    }
}

One Note: My Setup is similar to zls ones. My Definition for the role (in my case I called it Group):

注意:我的设置类似于zls。我对这个角色的定义是(在我的例子中,我称它为Group):

Acme\Foundation\UserBundle\Entity\Group:
    type: entity
    table: sec_groups
    id: 
        id:
            type: integer
            generator: { strategy: AUTO }
    fields:
        name:
            type: string
            length: 50
        role:
            type: string
            length: 20
    manyToOne:
        parent:
            targetEntity: Group

And the userdefinition:

userdefinition:

Acme\Foundation\UserBundle\Entity\User:
    type: entity
    table: sec_users
    repositoryClass: Acme\Foundation\UserBundle\Entity\UserRepository
    id:
        id:
            type: integer
            generator: { strategy: AUTO }
    fields:
        username:
            type: string
            length: 30
        salt:
            type: string
            length: 32
        password:
            type: string
            length: 100
        isActive:
            type: boolean
            column: is_active
    manyToOne:
        group:
            targetEntity: Group
            joinColumn:
                name: group_id
                referencedColumnName: id
                nullable: false

Maybe this helps someone.

也许这可以帮助别人。

#3


3  

I developped a bundle.

我开发一个包。

You can find it at https://github.com/Spomky-Labs/RoleHierarchyBundle

您可以在https://github.com/Spomky-Labs/RoleHierarchyBundle找到它

#4


2  

My solution was inspired by the solution provided by zls. His solution worked perfectly for me, but the one-to-many relation between the roles meant having one huge role tree, which would become hard to maintain. Also, a problem might occur if two different roles wanted to inherit one same role (as there could only be one parent). That's why I decided to create a many-to-many solution. Instead of having only the parent in the role class, I have first put this in the role class:

我的解决方案受到zls提供的解决方案的启发。他的解决方案对我来说非常有效,但是角色之间的一对多关系意味着拥有一个巨大的角色树,这将很难维护。而且,如果两个不同的角色想继承一个相同的角色(因为可能只有一个父角色),可能会出现问题。这就是为什么我决定创建多对多的解决方案。我没有在角色类中只包含父类,而是将它放在角色类中:

/**
 * @ORM\ManyToMany(targetEntity="Role")
 * @ORM\JoinTable(name="role_permission",
 *      joinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="permission_id", referencedColumnName="id")}
 *      )
 */
protected $children;

After that I rewrote the buildRolesTree function like so:

之后我重写了buildRolesTree函数,如下所示:

private function buildRolesTree()
{
    $hierarchy = array();
    $roles = $this->em->createQuery('select r, p from AltGrBaseBundle:Role r JOIN r.children p')->execute();

    foreach ($roles as $role)
    {
        /* @var $role Role */
        if (count($role->getChildren()) > 0)
        {
            $roleChildren = array();

            foreach ($role->getChildren() as $child)
            {
                /* @var $child Role */
                $roleChildren[] = $child->getRole();
            }

            $hierarchy[$role->getRole()] = $roleChildren;
        }
    }

    return $hierarchy;
}

The result is the ability to create several easily maintained trees. For instance, you can have a tree of roles defining the ROLE_SUPERADMIN role and entirely separate tree defining a ROLE_ADMIN role with several roles shared between them. Although circular connections should be avoided (roles should be laid out as trees, without any circular connections between them), there should be no problems if it actually happens. I haven't tested this, but going through the buildRoleMap code, it is obvious it dumps any duplicates. This should also mean it won't get stuck in endless loops if the circular connection occurs, but this definitely needs more testing.

其结果是能够创建几个易于维护的树。例如,您可以拥有定义ROLE_SUPERADMIN角色的角色树,以及定义ROLE_ADMIN角色的完全独立的树,这些角色之间共享多个角色。虽然应该避免循环连接(角色应该以树的形式布置,它们之间没有任何循环连接),但是如果实际发生的话,应该没有问题。我还没有对它进行测试,但是通过buildRoleMap代码,显然它会转储任何副本。这也意味着,如果循环连接发生,它不会陷入无休止的循环中,但这肯定需要更多的测试。

I hope this proves helpful to someone.

我希望这对某人有帮助。

#5


0  

Since role hierarchy don't change often, this a quick class to cache to memcached.

由于角色层次结构不会经常改变,所以这是一个快速缓存到memcached的类。

<?php

namespace .....;

use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Lsw\MemcacheBundle\Cache\MemcacheInterface;

/**
 * RoleHierarchy defines a role hierarchy.
 */
class RoleHierarchy implements RoleHierarchyInterface
{
    /**
     *
     * @var MemcacheInterface 
     */
    private $memcache;

    /**
     *
     * @var array 
     */
    private $hierarchy;

    /**
     *
     * @var array 
     */
    protected $map;

    /**
     * Constructor.
     *
     * @param array $hierarchy An array defining the hierarchy
     */
    public function __construct(array $hierarchy, MemcacheInterface $memcache)
    {
        $this->hierarchy = $hierarchy;

        $roleMap = $memcache->get('roleMap');

        if ($roleMap) {
            $this->map = unserialize($roleMap);
        } else {
            $this->buildRoleMap();
            // cache to memcache
            $memcache->set('roleMap', serialize($this->map));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getReachableRoles(array $roles)
    {
        $reachableRoles = $roles;
        foreach ($roles as $role) {
            if (!isset($this->map[$role->getRole()])) {
                continue;
            }

            foreach ($this->map[$role->getRole()] as $r) {
                $reachableRoles[] = new Role($r);
            }
        }

        return $reachableRoles;
    }

    protected function buildRoleMap()
    {
        $this->map = array();
        foreach ($this->hierarchy as $main => $roles) {
            $this->map[$main] = $roles;
            $visited = array();
            $additionalRoles = $roles;
            while ($role = array_shift($additionalRoles)) {
                if (!isset($this->hierarchy[$role])) {
                    continue;
                }

                $visited[] = $role;
                $this->map[$main] = array_unique(array_merge($this->map[$main], $this->hierarchy[$role]));
                $additionalRoles = array_merge($additionalRoles, array_diff($this->hierarchy[$role], $visited));
            }
        }
    }
}

#6


-3  

I hope this will help you.

我希望这对你有帮助。

function getRoles()
{

  //  return array(1=>'ROLE_ADMIN',2=>'ROLE_USER'); 
   return array(new UserRole($this));
}

You can get a good idea from, Where to define security roles?

您可以从何处定义安全角色得到一个好主意?

http://php-and-symfony.matthiasnoback.nl/ ( 2012 July 28 )

http://php-and-symfony.matthiasnoback。nl/(2012年7月28日)

#1


36  

The solution was simple. First I created a Role entity.

解决方案很简单。首先,我创建了一个角色实体。

class Role
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string $name
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @ORM\ManyToOne(targetEntity="Role")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
     **/
    private $parent;

    ...
}

after that created a RoleHierarchy service, extended from the Symfony native one. I inherited the constructor, added an EntityManager there and provided an original constructor with a new roles array instead of the old one:

在创建了一个RoleHierarchy服务之后,从Symfony本地服务扩展。我继承了构造函数,在那里添加了一个EntityManager,并提供了一个具有新的角色数组的原始构造函数,而不是旧的角色数组:

class RoleHierarchy extends Symfony\Component\Security\Core\Role\RoleHierarchy
{
    private $em;

    /**
     * @param array $hierarchy
     */
    public function __construct(array $hierarchy, EntityManager $em)
    {
        $this->em = $em;
        parent::__construct($this->buildRolesTree());
    }

    /**
     * Here we build an array with roles. It looks like a two-levelled tree - just 
     * like original Symfony roles are stored in security.yml
     * @return array
     */
    private function buildRolesTree()
    {
        $hierarchy = array();
        $roles = $this->em->createQuery('select r from UserBundle:Role r')->execute();
        foreach ($roles as $role) {
            /** @var $role Role */
            if ($role->getParent()) {
                if (!isset($hierarchy[$role->getParent()->getName()])) {
                    $hierarchy[$role->getParent()->getName()] = array();
                }
                $hierarchy[$role->getParent()->getName()][] = $role->getName();
            } else {
                if (!isset($hierarchy[$role->getName()])) {
                    $hierarchy[$role->getName()] = array();
                }
            }
        }
        return $hierarchy;
    }
}

... and redefined it as a service:

…并将其重新定义为服务:

<services>
    <service id="security.role_hierarchy" class="Acme\UserBundle\Security\Role\RoleHierarchy" public="false">
        <argument>%security.role_hierarchy.roles%</argument>
        <argument type="service" id="doctrine.orm.default_entity_manager"/>
    </service>
</services>

That's all. Maybe, there is something unnecessary in my code. Maybe it is possible to write better. But I think, that main idea is evident now.

这是所有。也许,我的代码中有些不必要的东西。也许有可能写得更好。但我认为,现在的主要观点是显而易见的。

#2


13  

I had do the same thing like zIs (to store the RoleHierarchy in the database) but i cannot load the complete role hierarchy inside the Constructor like zIs did, because i had to load a custom doctrine filter inside the kernel.request event. The Constructor will be called before the kernel.request so it was no option for me.

我做了与zIs类似的事情(在数据库中存储RoleHierarchy),但是我不能像zIs那样在构造函数中加载完整的角色层次结构,因为我必须在内核中加载一个定制的doctrine过滤器。请求事件。构造函数将在内核之前被调用。请求,所以我没有选择。

Therefore I checked the security component and found out that Symfony calls a custom Voter to check the roleHierarchy according to the users role:

因此我检查了安全组件,发现Symfony调用了一个自定义投票者根据用户角色检查roleHierarchy:

namespace Symfony\Component\Security\Core\Authorization\Voter;

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;

/**
 * RoleHierarchyVoter uses a RoleHierarchy to determine the roles granted to
 * the user before voting.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class RoleHierarchyVoter extends RoleVoter
{
    private $roleHierarchy;

    public function __construct(RoleHierarchyInterface $roleHierarchy, $prefix = 'ROLE_')
    {
        $this->roleHierarchy = $roleHierarchy;

        parent::__construct($prefix);
    }

    /**
     * {@inheritdoc}
     */
    protected function extractRoles(TokenInterface $token)
    {
        return $this->roleHierarchy->getReachableRoles($token->getRoles());
    }
}

The getReachableRoles Method returns all roles the user can be. For example:

geinsurancableroles方法返回用户可以扮演的所有角色。例如:

           ROLE_ADMIN
         /             \
     ROLE_SUPERVISIOR  ROLE_BLA
        |               |
     ROLE_BRANCH       ROLE_BLA2
       |
     ROLE_EMP

or in Yaml:
ROLE_ADMIN:       [ ROLE_SUPERVISIOR, ROLE_BLA ]
ROLE_SUPERVISIOR: [ ROLE_BRANCH ]
ROLE_BLA:         [ ROLE_BLA2 ]

If the user has the ROLE_SUPERVISOR role assigned the Method returns the roles ROLE_SUPERVISOR, ROLE_BRANCH and ROLE_EMP (Role-Objects or Classes, which implementing RoleInterface)

如果用户拥有ROLE_SUPERVISOR角色,则该方法返回ROLE_SUPERVISOR、ROLE_BRANCH和ROLE_EMP(角色对象或类,实现RoleInterface)的角色

Furthermore this custom voter will be disabled if there is no RoleHierarchy defined in the security.yaml

此外,如果security.yaml中没有定义RoleHierarchy,则将禁用此自定义投票者

private function createRoleHierarchy($config, ContainerBuilder $container)
    {
        if (!isset($config['role_hierarchy'])) {
            $container->removeDefinition('security.access.role_hierarchy_voter');

            return;
        }

        $container->setParameter('security.role_hierarchy.roles', $config['role_hierarchy']);
        $container->removeDefinition('security.access.simple_role_voter');
    }

To solve my issue I created my own custom Voter and extended the RoleVoter-Class, too:

为了解决我的问题,我创建了自己的自定义投票者,并扩展了rolevoter类:

use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Acme\Foundation\UserBundle\Entity\Group;
use Doctrine\ORM\EntityManager;

class RoleHierarchyVoter extends RoleVoter {

    private $em;

    public function __construct(EntityManager $em, $prefix = 'ROLE_') {

        $this->em = $em;

        parent::__construct($prefix);
    }

    /**
     * {@inheritdoc}
     */
    protected function extractRoles(TokenInterface $token) {

        $group = $token->getUser()->getGroup();

        return $this->getReachableRoles($group);
    }

    public function getReachableRoles(Group $group, &$groups = array()) {

        $groups[] = $group;

        $children = $this->em->getRepository('AcmeFoundationUserBundle:Group')->createQueryBuilder('g')
                        ->where('g.parent = :group')
                        ->setParameter('group', $group->getId())
                        ->getQuery()
                        ->getResult();

        foreach($children as $child) {
            $this->getReachableRoles($child, $groups);
        }

        return $groups;
    }
}

One Note: My Setup is similar to zls ones. My Definition for the role (in my case I called it Group):

注意:我的设置类似于zls。我对这个角色的定义是(在我的例子中,我称它为Group):

Acme\Foundation\UserBundle\Entity\Group:
    type: entity
    table: sec_groups
    id: 
        id:
            type: integer
            generator: { strategy: AUTO }
    fields:
        name:
            type: string
            length: 50
        role:
            type: string
            length: 20
    manyToOne:
        parent:
            targetEntity: Group

And the userdefinition:

userdefinition:

Acme\Foundation\UserBundle\Entity\User:
    type: entity
    table: sec_users
    repositoryClass: Acme\Foundation\UserBundle\Entity\UserRepository
    id:
        id:
            type: integer
            generator: { strategy: AUTO }
    fields:
        username:
            type: string
            length: 30
        salt:
            type: string
            length: 32
        password:
            type: string
            length: 100
        isActive:
            type: boolean
            column: is_active
    manyToOne:
        group:
            targetEntity: Group
            joinColumn:
                name: group_id
                referencedColumnName: id
                nullable: false

Maybe this helps someone.

也许这可以帮助别人。

#3


3  

I developped a bundle.

我开发一个包。

You can find it at https://github.com/Spomky-Labs/RoleHierarchyBundle

您可以在https://github.com/Spomky-Labs/RoleHierarchyBundle找到它

#4


2  

My solution was inspired by the solution provided by zls. His solution worked perfectly for me, but the one-to-many relation between the roles meant having one huge role tree, which would become hard to maintain. Also, a problem might occur if two different roles wanted to inherit one same role (as there could only be one parent). That's why I decided to create a many-to-many solution. Instead of having only the parent in the role class, I have first put this in the role class:

我的解决方案受到zls提供的解决方案的启发。他的解决方案对我来说非常有效,但是角色之间的一对多关系意味着拥有一个巨大的角色树,这将很难维护。而且,如果两个不同的角色想继承一个相同的角色(因为可能只有一个父角色),可能会出现问题。这就是为什么我决定创建多对多的解决方案。我没有在角色类中只包含父类,而是将它放在角色类中:

/**
 * @ORM\ManyToMany(targetEntity="Role")
 * @ORM\JoinTable(name="role_permission",
 *      joinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="permission_id", referencedColumnName="id")}
 *      )
 */
protected $children;

After that I rewrote the buildRolesTree function like so:

之后我重写了buildRolesTree函数,如下所示:

private function buildRolesTree()
{
    $hierarchy = array();
    $roles = $this->em->createQuery('select r, p from AltGrBaseBundle:Role r JOIN r.children p')->execute();

    foreach ($roles as $role)
    {
        /* @var $role Role */
        if (count($role->getChildren()) > 0)
        {
            $roleChildren = array();

            foreach ($role->getChildren() as $child)
            {
                /* @var $child Role */
                $roleChildren[] = $child->getRole();
            }

            $hierarchy[$role->getRole()] = $roleChildren;
        }
    }

    return $hierarchy;
}

The result is the ability to create several easily maintained trees. For instance, you can have a tree of roles defining the ROLE_SUPERADMIN role and entirely separate tree defining a ROLE_ADMIN role with several roles shared between them. Although circular connections should be avoided (roles should be laid out as trees, without any circular connections between them), there should be no problems if it actually happens. I haven't tested this, but going through the buildRoleMap code, it is obvious it dumps any duplicates. This should also mean it won't get stuck in endless loops if the circular connection occurs, but this definitely needs more testing.

其结果是能够创建几个易于维护的树。例如,您可以拥有定义ROLE_SUPERADMIN角色的角色树,以及定义ROLE_ADMIN角色的完全独立的树,这些角色之间共享多个角色。虽然应该避免循环连接(角色应该以树的形式布置,它们之间没有任何循环连接),但是如果实际发生的话,应该没有问题。我还没有对它进行测试,但是通过buildRoleMap代码,显然它会转储任何副本。这也意味着,如果循环连接发生,它不会陷入无休止的循环中,但这肯定需要更多的测试。

I hope this proves helpful to someone.

我希望这对某人有帮助。

#5


0  

Since role hierarchy don't change often, this a quick class to cache to memcached.

由于角色层次结构不会经常改变,所以这是一个快速缓存到memcached的类。

<?php

namespace .....;

use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
use Lsw\MemcacheBundle\Cache\MemcacheInterface;

/**
 * RoleHierarchy defines a role hierarchy.
 */
class RoleHierarchy implements RoleHierarchyInterface
{
    /**
     *
     * @var MemcacheInterface 
     */
    private $memcache;

    /**
     *
     * @var array 
     */
    private $hierarchy;

    /**
     *
     * @var array 
     */
    protected $map;

    /**
     * Constructor.
     *
     * @param array $hierarchy An array defining the hierarchy
     */
    public function __construct(array $hierarchy, MemcacheInterface $memcache)
    {
        $this->hierarchy = $hierarchy;

        $roleMap = $memcache->get('roleMap');

        if ($roleMap) {
            $this->map = unserialize($roleMap);
        } else {
            $this->buildRoleMap();
            // cache to memcache
            $memcache->set('roleMap', serialize($this->map));
        }
    }

    /**
     * {@inheritdoc}
     */
    public function getReachableRoles(array $roles)
    {
        $reachableRoles = $roles;
        foreach ($roles as $role) {
            if (!isset($this->map[$role->getRole()])) {
                continue;
            }

            foreach ($this->map[$role->getRole()] as $r) {
                $reachableRoles[] = new Role($r);
            }
        }

        return $reachableRoles;
    }

    protected function buildRoleMap()
    {
        $this->map = array();
        foreach ($this->hierarchy as $main => $roles) {
            $this->map[$main] = $roles;
            $visited = array();
            $additionalRoles = $roles;
            while ($role = array_shift($additionalRoles)) {
                if (!isset($this->hierarchy[$role])) {
                    continue;
                }

                $visited[] = $role;
                $this->map[$main] = array_unique(array_merge($this->map[$main], $this->hierarchy[$role]));
                $additionalRoles = array_merge($additionalRoles, array_diff($this->hierarchy[$role], $visited));
            }
        }
    }
}

#6


-3  

I hope this will help you.

我希望这对你有帮助。

function getRoles()
{

  //  return array(1=>'ROLE_ADMIN',2=>'ROLE_USER'); 
   return array(new UserRole($this));
}

You can get a good idea from, Where to define security roles?

您可以从何处定义安全角色得到一个好主意?

http://php-and-symfony.matthiasnoback.nl/ ( 2012 July 28 )

http://php-and-symfony.matthiasnoback。nl/(2012年7月28日)