phpzend-frameworkzend-framework2zfcuserzfc-rbac

ZF2 Generate navigation using zfcrbac zfcUser and hierarchical role strategy


I begin in zf2 and i have to handle Many to Many permissions/roles. Using ZfcRbac.

So i make a mapping with User / Hierarchical Role (table name= role) / permissions

And i make some guards with deny policy.

My guard, mapping, database, are ok.

My HierarchicalRole Entity looks like :

class HierarchicalRole implements HierarchicalRoleInterface

And it's, for now the same as the original given by Bakura.

My user Entity looks like this :

class User extends ZfcUserEntity implements IdentityInterface

with

    /**
 * @var \Doctrine\Common\Collections\Collection
 * @ORM\ManyToMany(targetEntity="HierarchicalRole")
 * @ORM\JoinTable(
 *     name="user_role_linker",
 *     joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="idUser")},
 *     inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
 * )
 */
protected $roles;

and roles are built in constructor by :

function __construct() {
    $this->roles = new ArrayCollection();
}

With zend developper tools i can see that ZfcRbac\Collector\RbacCollector show all i want for the actual logged user (permission, children, main role etc...).

My question is : How to generate a dynamic navigation, for a user wich can only see links that is granted to access ?. And also check connexion when the user not logged and hide it when he's logged...

I'm a newbie yet, but if possible it would be great to have an example well explained for doing a good dynamic navigation using this module.

EDIT (may 28) So far i always seek for a solution, my tries didn't helped me yet.. You can find one here : Spiffy navigation Still not working perfectly.


Solution

  • I'll show you how ZfcRbac can work with (ZF2) Zend/Navigation. You have definition of permissions in database, that's why I'll omit this section.

    Define your navigation adding pages and permissions:

    config/global.phpPHP:

    return array(
            'navigation' => array(
                'default' => array(
                    array(
                        'label' => 'Contracts',
                        'route' => 'contract',
                        'action' => 'list',
                        'permission' => 'contract.list',
                        'pages' => array(
                             array(
                                'label'  => 'New contract',
                                'route'  => 'contract',
                                'action' => 'add',
                                'permission' => 'contract.add',
                             )
                         )
                    )
                )
            ),
            'service_manager' => array(
                'factories' => array(
                    'navigation' => 'Zend\Navigation\Service\DefaultNavigationFactory',
                )
            )
        );
    

    Create a Listener (/module/Application/src/Application/Authorization/RbacListener.php):

    <?php
    
    namespace Application\Authorization;
    
    use Zend\EventManager\EventInterface;
    use Zend\Navigation\Page\AbstractPage;
    use ZfcRbac\Service\AuthorizationServiceInterface;
    
    class RbacListener
    {
        /**
         * @var AuthorizationServiceInterface
         */
        protected $authorizationService;
    
        /**
         * @param AuthorizationServiceInterface $authorizationService
         */
        public function __construct(AuthorizationServiceInterface $authorizationService)
        {
            $this->authorizationService = $authorizationService;
        }
    
        /**
         * @param  EventInterface $event
         * @return bool|void
         */
        public function accept(EventInterface $event)
        {
            $page = $event->getParam('page');
    
            if (!$page instanceof AbstractPage) {
                return;
            }
    
            $permission = $page->getPermission();
    
            if (is_null($permission)) {
                $event->stopPropagation();
                return false;
            }
    
            $event->stopPropagation();
    
            return $this->authorizationService->isGranted($permission);
        }
    }
    

    Create a Factory for the RbacListener (/module/Application/src/Application/Factory/RbacListenerFactory.php):

    <?php
    
    namespace Application\Factory;
    
    use Application\Authorization\RbacListener;
    use Zend\ServiceManager\FactoryInterface;
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class RbacListenerFactory implements FactoryInterface
    {
        /**
         * {@inheritDoc}
         */
        public function createService(ServiceLocatorInterface $serviceLocator)
        {
            $authorizationService = $serviceLocator->get('ZfcRbac\Service\AuthorizationService');
    
            return new RbacListener($authorizationService);
        }
    }
    

    Add your RbacListenerFactory to your ServiceManager (/module/Application/config/module.config.php):

    <?php
    
    return array(
        'service_manager' => array(
            'factories' => array(
                'Application\Authorization\RbacListener' => 'Application\Factory\RbacListenerFactory',
            ),
        ),
    );
    

    Attach an event to the isAllowed method of the Zend Navigation View Helper (And finally attach an event to the isAllowed method of the Zend Navigation View Helper):

    <?php
    
    public function onBootstrap(MvcEvent $event)
    {
        $application        = $event->getApplication();
        $eventManager       = $application->getEventManager();
        $sharedEventManager = $eventManager->getSharedManager;
        $serviceManager     = $application->getServiceManager();
        $rbacListener       = $serviceManager->get('Application\Authorization\RbacListener');
    
        $sharedEventManager->attach(
            'Zend\View\Helper\Navigation\AbstractHelper',
            'isAllowed',
            array($rbacListener, 'accept')
        );
    }
    

    To render menu in view or layout:

    <?php echo $this->navigation('navigation')->menu(); ?>
    

    I'm using this code and it works quite well. It's based on: