Inspired by this implementation of menu element for CakePHP I found that I require multilevel menus. It’s nice and can highlight current page. Furthermore it should fit drop down menu css layout. Now I need to show or hide element is there is authenticated user, but later I will need to hide menu items that are disabled by standard acl rules.
To achieve required results we should define:
- the component that will hold array of menu items;
- the helper class that will render menu in ul, li html tags;
- integrate existing css design of drop down menu.
The Dynamic Menu Component
We will crete menu.php file in the /app/controllers/components directory:
<?php class MenuComponent extends Object { var $components = array('Session'); function startup() { $userMenu = array(); $generalMenu = array(); $generalMenu[__('Home', true)] = '/'; if(!$this->Session->check('Auth.User')) { $userMenu[__('Register', true)] = '/users/register'; $userMenu[__('Login', true)] = '/users/login'; } else { $userMenu[__('Logout', true)] = '/users/logout'; $userMenu[__('Change', true)] = '/users/change'; } //sample child item $parent = array(); $parent[__('Child', true)] = '/'; $generalMenu[__('Parent', true)] = $parent; $user = $this->Session->read('Auth.User'); //menus arra $menus = array(); $menus[__('General', true)] = $generalMenu; $menus[__('User ', true)] = $userMenu; $this->Session->write('Menu.main', $menus); } } ?>
The component places menu array in session. We are going to show our menu on each page of our application. Therefore we will add the component to list of components in the AppController:
var $components = array('Menu');
Now we ready to start with the helper.
The Dynamic Menu Helper
To render our menu in format required by css we will create a helper. The render function will receive array of menus and return html of menus. Create /app/views/helpers/menu.php:
<?php /** * Dynamic menu helper library. * Can be used with CSS from http://www.tanfa.co.uk/css/examples/menu/ * * Methods to render dynamic menu . */ class MenuHelper extends AppHelper { var $helpers = array('Html'); /** * Render menu from session variable array. * Top item of the array is menu caption and div id * Sub items are menu items caption and link to the location, or array of subitems. * * @param array $menu Name of the session variable that stores menu array. * @static */ function render($menu = null) { $out = ''; foreach($menu as $caption => $config) { $out = $out.'<li><h2>'.$caption.'</h2>'. $this->Html->nestedList($this->parse($config)).'</li>'; } $out = '<ul>'.$out.'</ul>'; $out = $this->Html->div('menu', $out); return $out; } /** * Transforms configuration array in to array of hyperlinks recursively. * Returns arraly of list items. */ function parse($config) { $out = array(); $here = Router::url(substr($this->here, strlen($this->webroot)-1)); foreach($config as $caption => $link) { if (is_array($link)) { $out[$this->Html->div('parent', $caption)] = $this->parse($link); } else { if (Router::url($link) != $here) { $out[$caption] = $this->Html->link($caption, $link); } else { $out[$caption] = $this->Html->div('current', $caption); } } } return $out; } } ?>
As we are going to draw our menu in the default layout to display in on each page we will add the helper to the list of helpers in the AppController:
var $helpers = array('Menu');
Adding CSS of dynamic dropdown menu
You almost ready to view your menu, but first we have to add a css to our layout. In the /app/views/layouts/default.ctp add line o the head section of html:
echo $html-->css('menu');
Then create file /app/webroot/css/menu.css:
/* SVN FILE: $Id: misc.css 6311 2008-01-02 06:33:52Z phpnut $ */ /** * Menu App CSS */ .menu { width:12em; } .menu ul { list-style-image:none; list-style-position:outside; list-style-type:none; margin:0pt; padding:0pt; } .menu a, .menu h2, .menu .current, .menu .parent { border-color:#CCCCCC rgb(136, 136, 136) rgb(85, 85, 85) rgb(187, 187, 187); border-style:solid; border-width:1px; display:block; font-family:arial,helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; line-height:16px; margin:0pt; padding:2px 3px; } .menu h2 { background:#000000 none repeat scroll 0%; color:#FFFFFF; text-transform:uppercase; } .menu a, .menu .current, .menu .parent { background:#EFEFEF none repeat scroll 0%; color:#000000; text-decoration:none; } .menu a:hover, .menu .current, .menu .parent:hover { background:#FFFFFF none repeat scroll 0%; color:#AA0000; } .menu li { position:relative; padding:0; margin: 0; } .menu ul ul ul { left:100%; position:absolute; top:0pt; width:100%; } div.menu ul ul ul, div.menu ul ul li:hover ul ul { display:none; } div.menu ul ul li:hover ul, div.menu ul ul ul li:hover ul { display:block; }
Display dynamic menu on the default layout
The last step is to invoke our helper to render the menu. Add to the body section of layout this line:
echo $menu->render($session->read('Menu.main'));
That’s all! Now you should see the menu on each page of your application.

Later I will add acl checks to menu items.
April 12th, 2008 at 7:22 am
Nice!
Instructions are great!
I got it running in 5 minutes.
Do you know if I can get it to display horizontaly… fairly quickly?
Thanks
April 12th, 2008 at 8:35 am
Yes, have a look at refference url for horizontal css and other css tricks.
Here is direct link:
http://www.seoconsultants.com/css/menus/horizontal/
May 26th, 2008 at 6:54 am
in my web page still error
like this
Notice: Undefined variable: menu in /var/www/projects/alphasys/app/views/layouts/default.thtml on line 145
Fatal error: Call to a member function render() on a non-object in /var/www/projects/alphasys/app/views/layouts/default.thtml on line 145
May 26th, 2008 at 10:16 am
In the AppController helpers list you should add the ‘Menu’ helper.
June 28th, 2008 at 7:48 pm
Sweet! So easy to implement.
I had been struggling on whether to use a helper, element, etc. for my menu. I create websites where the owners can control their content themselves - a basic CMS app. (who wants to update a typo or add in a new page every other day?), So in order to accommodate the users, this is what I did…
In the afterSave function of my CMS management:
Menu information is cached when user updates the webpages (changes the title, adds a new page, etc.)
In startup function of menu component:
1. Read the menu information from cached menu. Split information into two arrays: main ($generalMenu) and side ($sideMenu)
2. If logged in, add the admin menu to the $userMenu (I use the $permittedControllers variable from this tutorial http://www.studiocanaria.com/articles/cakephp_auth_component_users_groups_permissions_revisited)
Added the appropriate css styling (the links above were great for this).
And voila! A beautiful site with functionality!
Thanks a million!
June 28th, 2008 at 8:02 pm
A small change I made to the parse function in the helper to indicate if the parent is the selected page:
function parse($config) {
$out = array();
$here = Router::url(substr($this->here, strlen($this->webroot)-1));
foreach($config as $caption => $link) {
if (is_array($link)) {
if (Router::url($link) != $here) {
$out[$this->Html->div(’current’, $caption)] = $this->parse($link);
} else {
$out[$this->Html->div(’parent’, $caption)] = $this->parse($link);
}
}
else {
if (Router::url($link) != $here) {
$out[$caption] = $this->Html->link($caption, $link);
}
else {
$out[$caption] = $this->Html->div(’current’, $caption);
}
}
}
return $out;
}
August 26th, 2008 at 10:07 am
Its a nice tut. I am new to cakePHP, wanted to know what if my parent menu items are coming from ‘Modules’ table and Child menu items are coming from ‘Forms’ table. The rights can me managed upto submenu level.
How can i Do this in cakePHP ?
Thanks
August 26th, 2008 at 12:38 pm
Well, in my point of view good solution is to link each menu item to the ACL item of Auth behaviour and check user permissions when building menu as well as checking user permission when he is requesting functionality (double check - one to list available functionality in menu and another to prevent user from accessing restricted functionality).
About Auth behaviour teke a look at http://lemoncake.wordpress.com/2007/07/15/using-aclbehavior-in-cakephp-12/
April 24th, 2009 at 2:32 pm
Congratulations for the initiative,
Test the code and run in firefox but usually in Internet Explorer is not working, someone help me?
Thanks
April 24th, 2009 at 2:46 pm
Look for IE fix section at this page
http://www.seoconsultants.com/css/menus/tutorial/
April 24th, 2009 at 7:17 pm
Thank you for help and sorry my bad English because living in Brazil
and not dominate the English.
Thanks
April 24th, 2009 at 9:25 pm
No problem. Hope this helps.
September 22nd, 2009 at 2:47 am
Hello!
Thanks for your code! It’s working, except that my old menu was built using data from a table (and not by writing code into a menu.ctp file), and I don’t know how to populate this new menu that I created using your tutorial.
I was passing an array called $categories to be the parent and an array called $pages to be the children. That was from the app_controller’s beforeFilter to the /views/elements/menu.ctp.
The problem is, the variables are not visible in the controllers/components/menu.php file.
Any help on that matter would be appreciated! Thanks! =)
September 29th, 2009 at 10:09 am
Hi!
You can access database from the component by instantiating model class ad invoking find and etc. like this:
$menu = ClassRegistry::init(’Menu’);
$items = $menu->findAll(…);
Hope this helps.
November 26th, 2009 at 12:02 pm
thanks..man
congratulations for great work…many other peoples confused me this is what i needed
December 2nd, 2009 at 4:43 pm
Thanks a loot for your solution and for your service, it helped me to improve my software solution based on cake.
In order to validate (w3c) my pages I have encountered a small problem with ampersant (&) and in particoular the code in the case ‘current’ daesn’t escape, so in my page souce I had ‘&’ instead of ‘&’, in ‘no current’ case the escape was right, so I wasn’t able to have a unique validated result.
I resolved this problem adding htmlentities PHP function in this way:
$out[$caption] = $this->Html->div(’current’, htmlentities($caption));
instead of the original:
$out[$caption] = $this->Html->div(’current’, $caption);
Thank again Polivoda you are great.
Babila Saronni
December 23rd, 2009 at 6:00 pm
Thank you very much for this nice component! Was really useful for me to build my menu!
February 13th, 2010 at 1:49 pm
this menu code,much help me .