Sunday, August 24, 2008

Prepending content

I have been coding in Zend for a while now. I've mostly been using the framework's reference documentation to guide me. I've also Googled when I need help but I've been a little disappointed with the number of examples I've been able to find online.

Anyway, here is a problem I ran into while working on working on Photoplate. Images are collected into albums. As a navigational aid I wanted to, whenever a user looks at an image, display the appropriate album information. So, in the image display function, I need to be able to call the display function in the AlbumController and prepend that content. It took me a while to figure out how to do this. I had no trouble calling another controller and appending that content but I couldn't figure out how to prepend the content. I fumbled around with the actionStack for a bit until I figured out the solution: named segements.

So, now, in my ImageController, I do this:


$this->_helper->actionStack('view', 'album', null, array('id' => $album_id, 'parent' => true));


This calls the view action in the AlbumController. Then, in my controller base class, I have the following code:


function postDispatch()
{
     // if the parent parameter is set to true, the requested action is rendered to the parent segement
     $parent = $this->_request->getParam('parent');
     if($parent) {
         $response = $this->getResponse();
         $view = $this->initView();
         $script = $this->getViewScript();
         // make sure to prepend this content to any previous parent content
         $previous_parent_body = $response->getBody('parent');
         $response->setBody($view->render($script), 'parent');
         $response->appendBody($previous_parent_body, 'parent');
         $this->_helper->viewRenderer->setNoRender();
    }
}


That's it! Because I have this postDispatch() function set up in my base class, I can prepend the content from any controller by adding a single line of code. Whenever the 'parent' parameter is set, the view is rendered to the 'parent' segement. In the above code, you can see that I fetch the current 'parent' content, swap in the newly generated content, and append the previous content. This is because the function calls work upwards from the bottom of the hierarchy and so the order of the content needs to be reversed. The above code would be a little simpler if the response object offered a prependBody() method and I may subclass the base class to add that method.