override CMenu isItemActive() to activate menu according to GET parameter

hi all,

I ran into a situation in which I am using CMenu [very nice widget] for my main menu. However, three of my menu options use the same controller/action and only different id parameter. So when I clicked one of the three menu options as a result all three were marked as active. So I decided to extend the CMenu class and customize it in a way that Yii decides which option is active according to the ‘id’ attribute. If you know a better solution please share.

I created a new file in components and named it CCustomMenu.php

In the layouts/main.php file make sure that the label of the menu is identical to the id attribute:


<?php $this->widget('CCustomMenu',array(

			'items'=>array(

				array('label'=>'Home', 'url'=>array('/site/index','id'=>'Home')),

                                array('label'=>'Services', 'url'=>array('/site/index','id'=>'Services')),

                                array('label'=>'Projects', 'url'=>array('/site/index','id'=>'Projects')),

				array('label'=>'About us', 'url'=>array('/site/page', 'view'=>'about')),

				array('label'=>'Contacts', 'url'=>array('/site/contact')),

				array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>Yii::app()->user->isGuest),

				array('label'=>'Изход ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!Yii::app()->user->isGuest)

			),

		)); ?>

in my CCustomMenu.php I override the isItemActive() function to change the logic a bit:


protected function isItemActive($item,$route)

	{


                 if(isset($item['url']) && isset($item['label']) && is_array($item['url']) && isset($item['id']) && strtolower($item['label'])===strtolower($item['id']))

                    {

                     if(count($item['url'])>1)

			{

				foreach(array_splice($item['url'],1) as $name=>$value)

				{

					if(!isset($_GET[$name]) || $_GET[$name]!=$value)

						return false;

				}

			}

			return true;

                 }elseif(isset($item['url']) && is_array($item['url']) && !strcasecmp(trim($item['url'][0],'/'),$route))

                         {

                     if(count($item['url'])>1)

			{

				foreach(array_splice($item['url'],1) as $name=>$value)

				{

					if(!isset($_GET[$name]) || $_GET[$name]!=$value)

						return false;

				}

			}

			return true;

		}

		return false;

                 }

Basically, I included one more if logic to check if label===id and switch to active. If anybody has a better idea please share with me. If not I hope this can be usefull for other people too.

cheers,

b

Thanks for sharing. You also also directly configure the ‘active’ property of the menu items to cope with complex conditions.

Thanks for the tip! :)

This is my overridden isItemActive function:


	protected function isItemActive($item, $route) {

    	if (isset($item['id'])) {

        	if($route === $item['id'])

            	return true;

        	return false;

    	}

    	return false;

	}



It requires that the ‘id’ attribute of the menu entries are set to the route.

For example:


                	'items' => array(

                    	array('label' => 'Home', 'url' => array('/site/index'), 'id' => 'site/index'),

                    	array('label' => 'Projects', 'url' => array('/projects/'), 'id' => 'project/index'),

                    	array('label' => 'Overview', 'url' => array('/projects/' . $_GET['name']), 'id' => 'project/view'),

                    	array('label' => 'Activity', 'url' => array('/projects/' . $_GET['name'] . '/activity'), 'id' => 'project/activity'),

                    	array('label' => 'Roadmap', 'url' => array('/projects/' . $_GET['name'] . '/roadmap'), 'id' => 'project/roadmap'),

                	),



I have custom URL rules, btw. Hence the use of $_GET[‘name’] …