CakePHP: Smarter links
The Problem
There's a couple of things I wish were present in CakePHP's excellent HtmlHelper.
- Links should automatically add a class to themselves when they are a) pointing to a resource in the same controller and b) pointing to themselves.
- Able to show/hide themselves.
Why is #1 important? Imagine a primary navigation that runs across the top of a page:
[Home] [Articles] [About]
If the links knew what section they pointed to you could add CSS rules to highlight the homepage link when the user was on the homepage, the Article link when the user was on *any* page handled by the ArticleController, etc.
And #2? If you could easily toggle their visibility you could for example, only show edit links to users with edit privileges.
The Solution
Enter the LinkHelper. This helper has only one method (link) which does all the work we need:
(note, minor update to handle urls passed as an array)
app/views/helpers/link.php
class LinkHelper extends AppHelper { var $helpers = array( "Html" ); function link($title, $url = null, $display = true, $htmlAttributes = array(), $confirmMessage = false, $escapeTitle = true) { if (!$display) { // do not display this link return ""; } else { // display the link // parse the current url into its components $here = Router::parse($this->params['url']['url']); // parse the destination url into its components if (is_array($url)) { $destination = $url; } else { $destination = Router::parse($url); } if (!isset($destination['controller'])) { $destination['controller'] = $this->params['controller']; } $class = ""; if ($here['controller'] == $destination['controller']) { // link is to another action within this controller $class .= " current_controller"; if ($here['action'] == $destination['action']) { // link is to the current action in this controller $class .= " current_action"; } if (isset($htmlAttributes['class'])) { // we already have a class attribute, append our classes $htmlAttributes['class'] .= $class; } else { // class not set, add ours $htmlAttributes['class'] = trim($class); } } // build the link $link = $this->Html->link($title, $url, $htmlAttributes, $confirmMessage, $escapeTitle); // return the link return $this->output($link); } } }
What Is It Doing?
Firstly, its checks the $display parameter. If it's false, then it returns nothing.
Why is this useful? It means you can attach a rule to your links to determine if they should be rendered or not:
<?php echo $link-link("Edit this article", "/articles/edit/1", $is_admin) ?>
The link will only be visible when $is_admin is true. $is_admin is a view variable I set in my app's as part of the authentication process to indicate whether or not the current user has admin privileges.
You can embed other logic to show/hide the link too:
<?php echo $link-link("Edit this article", "/articles/edit/1", ($article['Article']['user_id'] == $auth_user['User']['id'])) ?>
The link will only be visible when the current user is the author of the article.
Secondly, the method checks if the link points to a page handled by the current controller. If it does, it appends "current_controller" to the class of the link.
This is useful for those primary nav links. The current link will have a class of "current_controller" and can be styled differently to the others (very handy for tabs!)
<ul> <li><?echo $link->link("Home", "/") ?></li> <li><?echo $link->link("Articles", "/articles/" ?></li> <li><?echo $link->link("About", "/about" ?></li> </ul>
Thirdly, the method checks if the link points to the current action of the current controller. If it does, it appends "current_action" to the class of the link.


Comments
A minor tweak...
Updated the helper to handle urls passed as array("controller"=>"foo", "action"=>"bar")
Hey thanks, This is an
Hey thanks,
This is an great helper.......
Thanks a lot..................
home page
seems it does not work when link is '/'
you
you mean:
app/views/helpers/link.php ??
Thanks for the correction
Thanks for the correction Chris :-)
some modifications
here is a small fix to make it identify the view pages for my cms ( like /contents/view/1 etc )
i added the part where it checks for the passed param matches and then adds an active class to the exiting classes.
Works like a charm on cake 1.3.4
Thanks you so much for this! It works like a charm on CakePHP 1.3.4.
I added some code so that it also recognize a webpage by its slug.
For example I have an articles controller, with a view action that has a slug parameter to know what article to show.
With this little extra a class is also added for the specific article showed on the page :
Thanks again for this simple but truly efficient helper ;)
I think you're missing some
I think you're missing some parentheses on the second and third PHP lines in your "secondly" example. Thanks for this tip though!
CakePHP 2.0
Hey thanks for the helper. By the way to make it cakePHP compliant you basically have to change line 14
$here = Router::parse($this->params['url']);
to
$here = $this->params->params;
Works for me. Oh and I used Rajesh Sharma's version.
Post new comment