CakePHP: Smarter links

Richard's picture

The Problem

There's a couple of things I wish were present in CakePHP's excellent HtmlHelper.

  1. 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.
  2. 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

Richard's picture

Thanks for the correction

Thanks for the correction Chris :-)

Anonymous's picture

you

you mean:

app/views/helpers/link.php ??

Anonymous's picture

home page

seems it does not work when link is '/'

Anonymous's picture

Hey thanks,   This is an

Hey thanks,
 
This is an great helper.......
 
Thanks a lot..................

Richard's picture

A minor tweak...

Updated the helper to handle urls passed as array("controller"=>"foo", "action"=>"bar")

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Post new comment

The content of this field is kept private and will not be shown publicly. If you have a Gravatar account, used to display your avatar.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <code>, <blockcode>, <c>, <cpp>, <drupal5>, <drupal6>, <java>, <javascript>, <php>, <python>, <ruby>. Beside the tag style "<foo>" it is also possible to use "[foo]".

More information about formatting options