CakePHP - JSON and XML response explained

The hows why trying to respond with JSON and XML in CakePHP By Yihang Ho

I've been trying out CakePHP lately. The getting started tutorial is very beginner-friendly. However, the guide on JSON and XML views is not very clear. After fooling around a bit, I finally figured out how it works.

Making request

A single action can be made to respond the beautiful HTML, and also machine-readable JSON and XML. For example, to access the index action of the posts controller, we usually request for http://www.example.com/posts/index. To request for the JSON or XML version, simply append .json or .xml to the end of the action name. For example, http://www.example.com/posts/index.json or http://www.example.com/posts/index.xml. Besides that, you can also add Accept: application/json or Accept: application/xml into the request header with or without appending .json or .xml to the action name. This can be simulated using curl:

curl -H Accept:application/json http://www.example.com/posts/index
# or
curl -H Accept:application/xml http://www.example.com/posts/index

Configuration

We need to tell CakePHP and the controllers involved to accept and process JSON and XML request. First, add

Router::parseExtensions('json', 'xml');

into app/Config/routes.php. Also, inside the class definition of all the controllers involved (for example in app/Controller/PostsController.php), insert

public $components = array('RequestHandler');

In general, there are two ways to 'tell' CakePHP how to output the JSON and XML when requested. First method is easier and does not involve another view for JSON and XML; the other does.

Using serialize key

Suppose we want to encode the following array into JSON and XML:

$output = array(
    "status" => "OK",
    "message" => "You are good",
    "content" => $content
);

We need to first set() all the keys in the array:

$this->set("status", "OK");
$this->set("message", "You are good");
$this->set("content", $content);
// Can also set() other variables that might not be used in the JSON or XML string
$this->set("other", "something");
$this->set("stuff", "else");
// or simply
$this->set($output);

and then tell CakePHP which keys should appear in the output array:

$this->set("_serialize", array("status", "message", "content"));

Notice that other variables set with set() method will not appear in the JSON or XML output unless they are inside _serialized array. Now, if you visit index.json or index.xml you can see the output equivalent to json_encode($output, JSON_PRETTY_PRINT);

Using views

I guess for most of the time, the method that does not involve a view should be sufficient. Other times, for example, if you want some data to appear in the HTML version, but not on the JSON and XML version, you might do the necessary data processing in the view. The views for JSON and XML are defined in app/Views/<controller name>/<json or xml>/<action name>.ctp. For example, app/Views/Posts/json/index.ctp. To enable the views for JSON and XML, you have to disable $this->set("_serialize", array(...));. Also, the view must generate a valid JSON or XML string, or else CakePHP will throw an error.

// app/Controller/PostsController.php
public function index() {
    $this->set('posts', $this->Post->find('all'));
}
// app/Views/Posts/json/index.ctp
<?php
for ($i=0; $i<count($posts); $i++)
{
    unset($posts[$i]["Post"]["created"]);
    unset($posts[$i]["Post"]["modified"]);
}
json_encode($posts);
?>

Example code

I have prepared a piece of example code as a continuation of the blog tutorial. The code is available here. You may git checkout no-view or git checkout custom-view to see the actual code for the serialize key method and custom view method.