Use the URL view helper, please

by Naneau

When writing views, some time sooner or later, you’re going to be creating links to your own pages. Because you don’t want to end up writing relative URLs (they will not work, because the browser will add them to the current path), you need to have some kind of a base URL. A lot of people start writing links like:

1
<a href="/my_app/user/edit/id/123/">click me!</a>

While they will function perfectly, there’s two problems, both related to manageability of your website. First is that if your base URL changes (the “/my_app/”), you will have to edit all of your views. Second is that if you use custom routes you may end up with outdated routes, or even worse, erroneous routes in your views. Again, this would mean that you’d have to edit your view scripts.

There is a (near-) perfect solution for this, in the form of a view helper. The URL view helper will create URLs for you, based on your custom routes, and using the base URL set in your front controller. So, the first example could be written as:

1
2
3
4
5
6
<a href="<?php echo $this->url(array(
'controller' => 'user',
'action' => 'edit',
'id' => '123'
));?>">click me!</a>
//line endings added for readability

Say, if you were to add a custom route like so:

1
2
3
4
5
6
7
8
$route = new Zend_Controller_Router_Route(
    'edit/:id',
    array(
        'controller' => 'user',
        'action'     => 'edit'
    )
);
$router->addRoute('userEdit', $route);

The view helper could generate a matching URL for that route. You do however have to tell the helper that you want to use that specific route (which is identified with “userEdit”):

1
2
3
4
<a href="<?php echo $this->url(array(
'id' => '123'
), 'userEdit');?>">click me!</a>
//note the second parameter for the helper

Static files

Now, this will work for all your actions, but there’s still the matter of static files, like images and javascript. If you want your application to be truly portable, I suggest writing a little view helper for static files. I personally use this one, which does nothing but add the base URL to a filename:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
/**
 * StaticFile.php
 *
 * @category   Naneau
 * @package    Naneau_View_Helper
 * @copyright  Copyright (c) 2007 Maurice Fonk - http://naneau.nl
 * @version    0.1.1
 */


/**
 * Naneau_View_Helper_StaticFile
 *
 * a view helper for static files
 *
 * @category   Naneau
 * @package    Naneau_View_Helper
 * @copyright  Copyright (c) 2007 Maurice Fonk - http://naneau.nl
 */

class Naneau_View_Helper_StaticFile {
   
    /**
     * get a link to a static file with the current baseUrl
     *
     * @param string $file
     * @return string
     */

    public function staticFile($file) {
        $file = trim($file);
        //trim the file name (just in case)
        $frontController = Zend_Controller_Front::getInstance();
        //front controller
        $baseUrl = $frontController->getBaseUrl();
        //base url for this application
        return $baseUrl . '/' . $file;
        //the link to the file, relative to the base url
    }
}

In your view scripts you could use that helper to create links to static files:

1
<link rel="stylesheet" type="text/css" href="<?php echo $this->staticFile('blah.css'); ?>" />

Alternatively, you can set a variable to your views that holds the base URL. You would then manually create your links. I personally tend to favor the previous approach, because it allows for less repetition, which is always good. You could for instance set such a variable in your bootstrap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
//view renderer (from the helper broker)
if (is_null($viewRenderer->view)) {
    $viewRenderer->init();
    //force instantiation of the view
}

$frontController = Zend_Controller_Front::getInstance();
//frontcontroller

//... additional front controller setup goes here ...

$frontController->setBaseUrl('/my_app');
//set your base URL here

$viewRenderer->view->baseUrl = $frontController->getBaseUrl();
//set baseUrl for views, retrieved from the front controller

$response = $frontController->dispatch();
//do... something!