yii : how to display the different menu(s) by user role?

Thu Ra picture Thu Ra · Sep 13, 2012 · Viewed 17.5k times · Source

Q : How can I show display different menu(s) by user role?

Description : the app has many roles. e.g HR manager, Account Manager, Operating Manager, Employee, Operator, ...., etc. I used rights and yii-user modules to create those roles. Those roles have different functions. So the app will show different menu for different user's role after logged in. Now, I can lock the function for different user. e.g when HR manger logged in, he/she can't route to other function of user role. But I don't know how to show the HR Menu for Hr Manager, only.

I am not a newbie for yii. but I'm a newbie for those modules (rihgts and yii-user).

Answer

Stu picture Stu · Sep 14, 2012

If you're using RBAC you can set the 'visible' param of CMenu items depending on the users privileges, for example;

$this->widget('zii.widgets.CMenu',array(
    'items'=>array(
        array(
            'label'=>'Home',
            'url'=>array('site/index'),
        ),
        array(
            'label'=>'HR',
            'url'=>array('/hr/index'),
            'visible'=>Yii::app()->user->checkAccess('hr')
        ),
        array(
            'label'=>'Accounts',
            'url'=>array('/account/index'),
            'visible'=>Yii::app()->user->checkAccess('account')
        ),
        array(
            'label'=>'Operations',
            'url'=>array('/operations/index'),
            'visible'=>Yii::app()->user->checkAccess('operations')
        ),
    ),
);

This way users will only be able to see the items in the menu if they have access privileges for that area.

[EDIT]

As per simaremare's comment below, you can force caching of this query beyond the current request by extending CWebUser. Firstly, set your user to run through your new class (we'll call it TWebUser), so in your main.php config file;

'components'=>array(
    'user'=>array(
        ...
        'class'=>'TWebUser',
        ...
    ),
    ...
),

Now we need to create TWebUser to cache these beyond the current request (which is what CWebUser does (source code):

class TWebUser extends CWebUser
{
    private $_access=array();

    public function checkAccess($operation,$params=array(),$allowCaching=true)
    {
        if($allowCaching && $params===array() && isset($this->_access[$operation]))
            return $this->_access[$operation];

        $cache = Yii::app()->session['checkAccess'];
        if($allowCaching && !$this->getIsGuest() && isset($cache[$operation]) && time() - $cache[$operation]['t'] < 1800)
        {
            $checkAccess = $cache[$operation]['p'];
        } else {
            $checkAccess = Yii::app()->getAuthManager()->checkAccess($operation,$this->getId(),$params);

            if($allowCaching && !$this->getIsGuest())
            {
                $access = isset($cache) ? $cache : array();
                $access[$operation] = array('p'=>$checkAccess, 't'=>time());
                Yii::app()->session['checkAccess'] = $access;
            }
        }

        return $this->_access[$operation] = $checkAccess;
    }
}

Now your access results will be set for the whole session. This does mean that if you edit the RBAC permissions for a given account, they'll have to log out and log in again to see the new changes reflected in the browser.

I hope that helps! I'm sure I found this workaround from someone else (probably on SO), but I can't find the original post to give them credit.