Default views for extensions

Published on and tagged with cakephp  view

I’m currently revamping the API of NoseRub to use CakePHP’s Router::parseExtensions() magic, i.e. if you append an extension like “.json” to the action you request, then CakePHP automagically sets the correct content type, and uses extension-specific layouts and views to render the result (e.g. if you request /example/action.json then the layout app/views/layouts/json/default.ctp and the view app/views/example/json/action.ctp are used by default).

That’s nice, however, in our case all JSON views are identical (the same applies for the XML views):

// requires the JSON part from the Zend framework
App::import('Vendor', 'json', array('file' => 'Zend'.DS.'Json.php'));
echo Zend_Json::encode(array('data' => $data));

And so following the default approach would lead to a lot of duplication and many unnecessary folders and view files. And that’s not that cool ;-)

After some experimentation I found a better way.

For each extension I created a folder with a default view:

app
  views
    json
      default.ctp
    xml
      default.ctp

json/default.ctp contains the code I have shown above, and xml/default.ctp would look like:

echo $xml->serialize(array('data' => $data), array('format' => 'tags'));

To tell CakePHP it should render those default views when there is not a more specific view, I added the following beforeRender() callback method to the AppController:

// app/app_controller.php
class AppController extends Controller {
    public function beforeRender() {
        $pathToViewFile = dirname(__FILE__).DS.'views'.DS.$this->viewPath.DS.$this->action.'.ctp';
		
        if (!file_exists($pathToViewFile)) {
            $this->viewPath = $this->layoutPath;
            $this->action = 'default';
        }
    }
}

Happy baking!

5 comments baked

  • Martin Westin

    Nice one.
    This technique looks very usable for most alternative extensions, at least most I use, are “data” formats.

  • poLK

    There is VIEWS constant, but it will not help much in case of multiple view paths or usage from plugins anyway.

  • poLK

    Also, changing $this->action might lead to some bugs hard to find, I would go for AppController::render() instead.

  • majna

    why Zend_Json::encode?
    when cake has $javascript->object($data);
    which uses PHP jason_encode if available.
    Is there any known issues with php jason_encode?
    Zend Jason class looks odd to me (recursion, Solar framework code for unicode, performance…).

  • cakebaker

    @all: Thanks for your comments!

    @poLK: Yes, overriding the render() method could be an alternative, though I wasn’t able to make this approach work…

    @majna: No, there is no issue with PHP’s json_encode (Zend/Json also uses this function if available). And the reason we use Zend/Json over $javascript->object() is that it automatically encodes special characters.

© daniel hofstetter. Licensed under a Creative Commons License