Rails-like url helper for Zend Framework

September 7th, 2008 jasoneisen

I’ve been doing a lot of research into rails lately, and one of it’s most useful features is it’s routing. It strictly follows the REST methodology, which can help make application design much simpler. If you’re not familiar with this, or have no idea what I’m talking about, heres a cheat sheet to help you out.

The Problem:

Spitting out a route in the view is so annoyingly verbose. As an example:

 
// /users
<?php echo $this->Url(array(), 'users_path', true); ?>
 
// /users/4/edit
<?php echo $this->Url(array('id' => 4), 'edit_user_path', true); ?>
 
// /company/123/users/4/edit
<?php echo $this->Url(array('company_id' => 123, 'id' => 4), 'edit_company_user_path', true); ?>

Do you see how the code to get a string is twice as long as the string itself? An even more annoying part is that the options array and the route name parameter order is backwards. If I want to spit out a named route without any options, I have to provide an empty array() to get to the name.

The Solution:

If you looked at the cheat sheet, you will have noticed how beautifully simple it is to print out a route in rails. Here’s one usage of my new alternative:

 
// /users
<?php echo $this->Path('users'); ?>
 
// /users/4/edit
<?php echo $this->Path('edit_user', 4); ?>
 
// /company/123/users/4/edit
<?php echo $this->Path('edit_company_user', array('company_id' => 123, 'id' => 4)); ?>

Note that this isn’t the final product, it gets better. But see how that cut down on code? It’s not _that_ much better, but it’s a start. The key feature here is putting the options array and route name in the right order, automatically adding “_path” to the route name (it’s the name of the function, cmon that would be redundant), making $reset true by default, and creating an array(’id’=>$arg) options array when the argument isn’t an array.

Here’s the code for the view helper:

 
class My_View_Helper_Path
{
    public function Path($name, $urlOptions = array(), $reset = true, $encode = true)
    {
        if (substr($name, -5) != '_path') {
            $name .= "_path";
        }
 
        if (!empty($urlOptions) && !is_array($urlOptions)) {
            $urlOptions = array('id' => $urlOptions);
        }
 
        $router = Zend_Controller_Front::getInstance()->getRouter();
 
        return $router->assemble($urlOptions, $name, $reset, $encode);
    }
}

Now if you look closely, you’ll wonder “why check for ‘_path’ on the route? I thought if we used that it was redundant?”. That’s because this is only the first half. I now introduce to you the second possible usage with a little more magic:

 
// /users
<?php echo $this->users_path(); ?>
 
// /users/4/edit
<?php echo $this->edit_user_path(4); ?>
 
// /company/123/users/4/edit
<?php echo $this->edit_company_user_path(array('company_id' => 123, 'id' => 4)); ?>

That, folks, is as close as the php language will let us come to rails, barring the removal of “echo” and having the method do that itself (in which case it would lose the functionality of being used in non view-specific tasks). You might be wondering what the hell I did to my view to get this to work. It was actually quite simple: make my own view class that overrides the __call() method.

Here’s the entire class in all it’s glory:

 
class My_View extends Zend_View
{
    public function __call($method, $args)
    {
        if (substr($method, -5) == '_path') {
            array_unshift($args, $method);
            $method = 'Path';
        }
 
        return parent::__call($method, $args);
    }
}

Just simply use this view instead of Zend_View. In case you don’t know how to do that, somewhere early on in your app’s loading process do the following:

 
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->setView(new My_View());

Note that the view helper will work perfectly by itself with the first example usage, no overriding the view is necessary.

Namespaces will make things like this a lot easier when they get here, but for now, this does the trick. Have fun :)

Update Sept 7, 2:12 pm:

I’ve written a quick little method in my base controller to use this that I thought I’d share:

 
    protected function _gotoPathAndExit($name = null, $urlOptions = array(), $reset = false)
    {
        $path = $this->view->Path($name, $urlOptions = array(), $reset = false);
        $this->_helper->redirector->setPrependBase(false);
 
        $this->_redirect($path);
    }
 
    // Old usage
    $this->getHelper('Redirector')->gotoRouteAndExit(array(), 'users_path');
 
    // New hotness
    $this->_gotoPathAndExit('users');

Tags: , , , , , , ,

Posted in REST, Zend | 3 Comments »

REST for Zend Framework

July 3rd, 2008 jasoneisen

I’ve created a small library to make my ZF apps more RESTful. If you’d like to take a look: http://code.google.com/p/restfulphp/.

Credit to Jani and David

Best examples I can provide at the moment are in the demo folder. The routes act nearly identical to those found in the pdf here.

$this->Url(array(), ‘plural_path’);
$this->Url(array(’id’=>$id), ‘edit_singular_path’);
etc…

I’ll post some better documentation and usage soon, but it’s a 3 day weekend coming up, plus it’s my birthday sat, so it may be a few days :)

Tags: , , , ,

Posted in Doctrine, REST, Zend | 2 Comments »