Handling relative links with Zend_View_Helper

It's 4:30 AM, and I'm sitting in an airport lounge waiting for a flight to Brussels after accidentally bumping into Zeev and a couple of other guys (Amnon and Matti) from Zend, on their way to a management meeting in the US. Since they had to board, and I'm left all alone and still have an hour or so to pass, I'm going to share a nifty little thing I found in Zend Framework a couple of days ago: A nice and clean way to generate relative links in view scripts.

Putting links (<a href="..."> tags or <link href="..."> tags for example) is quite easy if you assume your application is always installed on the document root of a virtual host - which is something many MVC applications assume these days. You just link to '/path' and you're done. But what happens if you want to have your application installed, linked or aliased to a subdirectory under your document root - something like 'http://prematureoptimization.org/blog/' ? Then you'd have to dynamically generate the path on every link you create. Here is how I've done it:

First, have some kind of config file (or just define in your bootstrap file) what is the path under your document root your application is - personally, I used Zend_Config to do that - but you don't have to.

Second, when you create your front controller, do the following:

PHP:
  1. <?php
  2. // $config->www->baseurl is the base URL of your app.
  3. $front = Zend_Controller_Front::getInstance();
  4. $front->setBaseUrl($config->www->baseurl);
  5. // ... continue the dispatch process
  6. ?>



Once you set Zend_Controller_Front->setBaseUrl() to point to your base URL, all redirections will work well when you call Zend_Controller_Action->_redirect() in your controllers.

But how do you create links in your views? Well, you could put your $config object in a registry and access it - but it's kind of ugly, plus it won't work if you override your $config somehow, or don't have a $config object at all.

Another thing I tried doing is calling Zend_Controller_Front->getBaseUrl() whenever I needed it - like so:

PHP:
  1. <a href="<?= Zend_Controller_Front::getInstance()
  2.                 ->getBaseUrl(); ?>/somepage">click here</a>


But again, this is a bit confusing, and it feels kind of awkward calling the front controller from view scripts - in my opinion, view scripts should be readable and non-intimidating so that even your average web designer could work with them (and not break them).

Well, following an advice from Matthew I created a view helper to handle the creation of relative links. It's important to say that Zend Framework already comes with a view helper that creates relative links - Zend_View_Helper_Url - but it's quite complex in a sense that it creates links from predefined routes using a Zend_Controller_Router object from your front controller. I needed something simpler, using static paths, and this is what I came up with:

PHP:
  1. <?php
  2. class Zend_View_Helper_LinkTo
  3. {
  4.     public function linkTo($path)
  5.     {
  6.         $root = '/' . trim(Zend_Controller_Front::getInstance()
  7.                             ->getBaseUrl(), '/');
  8.         if ($root == '/') $root = '';
  9.         return $root . '/' . ltrim($path, '/');
  10.     }
  11. }

Now all you have to do is put this class in a file named 'LinkTo.php' in your application/views/helpers directory (naming is important - see the Zend_View manual chapter on helpers for more information) you could just do this from your view scripts to get relative links easily:

PHP:
  1. <a href="<?= echo $this->linkTo('/somepage'); ?>">Some page</a>

Nice, huh?

4 Responses to “Handling relative links with Zend_View_Helper”

  1. May 7th, 2007 | 21:20

    I'd modify that slightly. View helpers are instantiated only once per view object -- meaning that if you're calling the helper multiple times, why not cache the baseUrl in the object during instantiation? :-) This will have the benefit of performing faster on subsequent invocations, bringing the total number of function calls down to basically only the method call.

  2. May 7th, 2007 | 21:38

    Yeah, I thought about that - and even better, cache it in a static property - so you're reducing calls from *all* view scripts, not just from each one separately. Something like this:

    PHP:
    1. class Zend_View_Helper_LinkTo
    2. {
    3.     protected static $baseurl = null;
    4.  
    5.     public function linkTo($path)
    6.     {
    7.         if (self::$baseurl === null) {
    8.             $root = '/' .
    9.                 trim(Zend_Controller_Front::getInstance()
    10.                 ->getBaseUrl(), '/');
    11.             if ($root == '/') $root = '';
    12.             self::$baseurl = $root . '/';
    13.         }
    14.        
    15.         return self::$baseurl . ltrim($path, '/');
    16.     }
    17. }
  3. April 1st, 2008 | 16:02

    [...] now I find the solution here: Premature Optimization Handling relative links with Zend_View_Helper uder the title: Handling relative links with [...]

  4. April 9th, 2008 | 15:41

    [...] take a look to this to see if it is your solution: Premature Optimization Handling relative links with Zend_View_Helper [...]