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
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:
/**
* @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:
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.
I hope this proves helpful to someone.